Go

From NoskeWiki
Revision as of 13:54, 6 February 2019 by NoskeWiki (talk | contribs) (Created page with "==About== Go, or "<b>Golang</b>", is an open source, compiled, garbage-collected, programming language, developed at <b>Google</b> by <i>Robert Griesemer</i>, <i>Rob Pike</i>...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

About

Go, or "Golang", is an open source, compiled, garbage-collected, programming language, developed at Google by Robert Griesemer, Rob Pike, and Ken Thompson, and released 2009. Although very new it's picking up popularity both within and outside of Google. The best resource for learning about go is golang.org, but here I've written some of my own notes for future reference.


Installing

Installing go is relatively straight forward.

  1. Go to the install page for instructions.
  2. Download the relevant tar.gz from the downloads page.
  3. Run: $ sudo tar -C /usr/local -xzf <your_download_dir>/go1.1.linux-amd64.tar.gz
  4. Open a bash terminal and run: $ export PATH=$PATH:/usr/local/go/bin
  5. Test: make a "hello.go" with the contents of the hello world below, then restart a terminal and run: $ go run hello.go

NOTE: If you have Mac OS the package installer makes it super easy. For Windows, it's ticker so follow instructions carefully.


Examples

NOTE: Most of these examples were modified or come directly from: http://tour.golang.org/ - a brilliant interactive tutorial which lets you run and change any example to see output within the browser - no install necessary. :)


Hello World

package main
import "fmt"
 
func main() {
  fmt.Println("Hello, World")
}

Importing Packages

Notice here:

  • import can be inside import()
  • imported packages are referred to by last part and all functions starting with a capital are accessible... hence "rand.Intn"
  • visibility of a name outside a package is determined by whether its first character is upper case
package main
import (
  "fmt"
  "time"
  "math/rand"
)
 
func main() {
  rand.Seed(time.Now().UTC().UnixNano())
  fmt.Println("My favorite number is", rand.Intn(10))  // Generates # between 0 and 9
}

Functions

A function can take zero or more arguments. Note that the type comes after the variable name, and if a sequence of vars are the same type you can write the type for the last one only: x, y int.

...
func add(x int, y int) int {
  return x + y
}
 
func main() {
  fmt.Println(add(42, 13))
}


Functions can also return multiple results, named or unnamed, as follows:

...
func swap(x, y string) (string, string) {  // Unnamed results
  return y, x
}
 
func splitInHalf(sum int) (x, y int) {     // Named results
  x = sum / 2
  y = sum - x
  return
}
 
func main() {
  a, b := swap("hello", "world")
  fmt.Println(a, b)            // Prints: hello world
  fmt.Println(splitInHalf(9))  // Prints: 4 5
}


Variables

  • You can use the "var" statement and then type (var i float64).
  • Inside a function the short := assignment can be used in place of var ( i := 1.0).
  • Variable, and all other names should be MixedCaps or mixedCaps rather than underscores to write multiword names.
package main
import "fmt"

var x, y, z int = 1, 2, 3
var c, python, java = true, false, "no!"
 
func main() {
  fmt.Println(x, y, z, c, python, java)  // Prints: 1 2 3 true false no!
  a, b, c := 42, 1.5, "hello"
  fmt.Println(a, b, c)                   // Prints: 42 1.5 hello
}

Types

Go's basic types are

bool
string
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32 represents a Unicode code point
float32 float64
complex64 complex128

Constants

Declared like variables, but with the const keyword, and cannot use the := syntax.

const Pi = 3.14
 
func main() {
  const World = "世界"
  fmt.Println("Hello", World)   // Prints: Hello 世界
  fmt.Println("I like", Pi)     // Prints: I like 3.14
}

Type Cast

  pi := 3.1231
  piAsInt := int(pi)    // Type casting is easy - this becomes 3


Loops ("For")

Go only uses "for" loops

  sum := 0
  for i := 0; i < 10; i++ {
    sum += i
  }
...
  sum := 1
  for sum < 1000 {     // Can drop unused ";" values.
    sum += sum
  }
...
  for {                // Use for infinite loop (with break inside).
    fmt.Println(".")
  }

If statement

Like C++, but the ( ) are missing and the { } are required.

package main
import (
  "fmt"
  "math"
)
 
func sqrt(x float64) string {
  if x < 0 {
    return sqrt(-x) + "i"
  } else {
    return fmt.Sprint(math.Sqrt(x))
  }
}
 
func main() {
  fmt.Println(sqrt(2), sqrt(-4))
}


Structs (there are no classes)

  • A struct is a collection of fields.
  • There are no classes.
...
type Point struct {
  X int
  Y int
}
 
func main() {
  v := Point{1, 2}   // Here's how you initialize all the values.
  v.X = 4
}

Arrays ("Slices")

  • Go says "slice" instead of array. Go figure.
  • An empty slice is "nil"
package main
import "fmt"
 
func main() {
  p := []int{2, 3, 5, 7, 11, 13}
  fmt.Println("p ==", p)                    // Prints: p == [2 3 5 7 11 13]
  for i := 0; i < len(p); i++ {
    fmt.Printf("p[%d] == %d\n", i, p[i])    // Prints: p[0] == 2 | p[1] == 3 | <...>
  }
 
  subsetSlice := p[1:4]             // Get between element 1 & 4
  fmt.Println(subsetSlice)          // Prints: p[1:4] == [3 5 7]
  fmt.Println("p[:3] ==", p[:3])    // Prints from beginning to 3rd element   [2 3 5]
  fmt.Println("p[4:] ==", p[4:])    // Prints 4th to lastelement              [11 13]
}

A nice trick is the "range" operation:

package main
import "fmt"
 
var pow = []int{1, 2, 4, 8}
func main() {
  for i, v := range pow {                // Range will take us to the end
    fmt.Printf("2**%d = %d\n", i, v)     // Prints: 2**0 = 1 | 2**1 = 2 | 2**2 = 4 | 2**3 = 8
  }
}


2D Array

package main
import (
  "fmt"
  "math/rand"
)  // NOTE: can use this syntax for importing libraries too.
 
func Pic(dx, dy int) [][]uint8 {
  p := make([][]uint8, dy)
  for i := range p {
    p[i] = make([]uint8, dx)  // Use this technique to setup 2D arrays.
  }
  for y := 0; y < dy; y++ {
    for x := 0; x < dx; x++ {
      p[y][x] = uint8(x+y + rand.Intn(10))   // Some noise.
    }
  }
  return p
}
 
func main() {
  picture := Pic(3,4)
  fmt.Println("picture ==", picture)  // Prints: [[1 8 9] [10 3 11] [7 3 10] [3 8 6]]
}

Maps

package main
import "fmt"
 
type Vertex struct {
  Lat, Long float64
}
 
var m map[string]Vertex
 
func main() {
  m = make(map[string]Vertex)     // Must make maps with this.
  m["Googleplex"] = Vertex{37.4221,-122.084064}
  fmt.Println(m["Googleplex"])    // Prints: {37.4221,-122.084064}
  val, present := m["Microplex"]  // Check if element exists: val = 0, present = false
  fmt.Println(val, " > ", present)  // Prints: {0 0}  >  false
  delete(m, "Googleplex")         // Delete element.
}

Here's a nice example of unique word counting:

package main
import "strings"
import "fmt"
 
func WordCount(s string) map[string]int {
  m := make(map[string]int)
  words := strings.Fields(s)
  for i := range words {
    m[words[i]]++
  }
  return m    // NOTE: For the input below, len(m) will be 4 (4 unique words).
}
 
func main() {
  fmt.Println(WordCount("to be or not to be"))  // Prints: map[to:2 be:2 or:1 not:1]
}

Case Statement

package main
import (
  "fmt"
  "time"
)
 
func main() {
  fmt.Println("When's Saturday?")
  today := time.Now().Weekday()
  switch time.Saturday {
    case today + 0:                 // Will go through first that falls though ("break" is inherent).
      fmt.Println("Today.")
    case today + 1:
      fmt.Println("Tomorrow.")
    case today + 2:
      fmt.Println("In two days.")
    default:                        // If no default provided it actually stalls - which can be handy for waiting on things.
      fmt.Println("Too far away.")
  }
}

Closure

A closure is a function value that references variables from outside its body. In this example, each close is bound to it's "prev" and "next" value.

package main
import "fmt"
 
func fibonacci() func() int {
  prev, curr := 0, 1  // This line is called only once.
  return func() int {
    next := prev + curr
    prev = curr
    curr = next
    return prev
  }
}
 
func main() {
  f := fibonacci()
  for i := 0; i < 10; i++ {
    fmt.Println(f())  // Prints: 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55
  }
}


Methods

Go does not have classes. However, you can define methods on struct types.

package main
import "fmt"
import "math"
 
type Vertex struct {
  X, Y float64
}
 
// Returns distance of point to origin (think hypotenuse).
func (v *Vertex) Abs() float64 {  // Behaves like a method for Vertex.
  return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
// Scale the vector
func (v *Vertex) Scale(f float64) {
  v.X = v.X * f
  v.Y = v.Y * f
}
 
func main() {
  v := &Vertex{3, 4}
  fmt.Println(v.Abs())     // Prints: 5
  v.Scale(5)
  fmt.Println(v, v.Abs())  // Prints: &{15 20} 25
}


Interfaces

An interface type is defined by a set of methods.

package main
import "fmt"
import "strconv"
 
type Shape interface {
  ToString() string      // Like creating an abstract method.
}
//-----------
type Circle struct { radius float64 }
 
func (c Circle) ToString() string {  // Implementation
  return "SQUARE: radius=" + strconv.FormatFloat(c.radius, 'f', 3, 64);
}
//-----------
type Rectangle struct { w, h int }
 
func (r Rectangle) ToString() string {  // Implementation
  return "RECTANGLE: " + strconv.Itoa(r.w) + "x" + strconv.Itoa(r.h)
}
//-----------
func main() {
  var s Shape
  circle := Circle{2.5}
  rectangle := Rectangle{3, 4}
 
  s = circle
  fmt.Println(s.ToString())  // Prints:  SQUARE: radius=2.500
  s = rectangle
  fmt.Println(s.ToString())  // Prints:  RECTANGLE: 3x4
}


package main
import "fmt"
import "os"
 
type Reader interface {
  Read(b []byte) (n int, err error)
}
 
type Writer interface {
  Write(b []byte) (n int, err error)
}
 
type ReadWriter interface {
  Reader
  Writer
}
 
func main() {
  var w Writer
  w = os.Stdout    // os.Stdout implements Writer
  fmt.Fprintf(w, "hello, writer\n")  // Prints: hello, writer
}


Errors

An error is anything that can describe itself as an error string. The idea is captured by the predefined, built-in interface type, error, with its single method, [Error], returning a string.

package main
import "fmt"
import "math"
 
type ErrNegativeSqrt struct { val float64 }
 
func (e ErrNegativeSqrt) Error() string {
  return fmt.Sprintf("Negative value: %v", e.val)
}
 
func Sqrt(f float64) (float64, error) {
  if f < 0 {
    return 0, ErrNegativeSqrt{f}
  }
  return math.Sqrt(f), nil
}
 
func main() {
  fmt.Println(Sqrt(2))   // Prints: 1.4142135623730951 <nil>   (nil error)
  fmt.Println(Sqrt(-2))  // Prints: 0 Negative value: -2       (error string)
}


package main
import "fmt"
import "time"
 
type MyError struct {
  When time.Time
  What string
}
 
func (e *MyError) Error() string {
  return fmt.Sprintf("at %v, %s", e.When, e.What)
}
 
func run() error {
  return &MyError{time.Now(), "it didn't work"}
}
 
func main() {
  if err := run(); err != nil {
    fmt.Println(err)  // Prints: at 2009-11-10 23:00:00 +0000 UTC, it didn't work
  }
}


Web Server

For these webserver examples, make sure GO is installed on your machine then once running visit http://localhost:4000 to see the greeting.

package main
 
import (
  "fmt"
  "net/http"
)
 
type Hello struct{}
 
func (h Hello) ServeHTTP(
  w http.ResponseWriter,
  r *http.Request) {
  fmt.Fprint(w, "Hello!")
}
 
func main() {
  var h Hello
  http.ListenAndServe("localhost:4000", h)
}


package main
import "fmt"
import "net/http"
 
type String string
 
func (s String) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, s)
}
 
type Struct struct {
  Greeting, Punct, Who string
}
 
func (s Struct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  out := s.Greeting + " " + s.Punct + " " + s.Who
  fmt.Fprint(w, s.Greeting, out)
}
 
func main() {
  http.Handle("/string", String("I'm a frayed knot."))
  http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})
  http.ListenAndServe("localhost:4000", nil)
}

Image

Go has it's own "Image" library you can use. You'll need another library like pic to draw it though.

package main
import "golang.org/x/tour/pic"  // If you want to draw.
import "image"
import "image/color"
 
type  MyColorImage struct{
  config image.Config        // Stores width, height and color model.
  backColor color.Color      // Background color.
}
 
func MakeColorImage(w, h int) MyColorImage {
  i := MyColorImage{}
  i.config.Width = w
  i.config.Height = h
  i.config.ColorModel = color.RGBA64Model
 
  i.backColor = color.RGBA{255, 0, 0, 255}    // Default this to red.
  return i
}
 
func (i  MyColorImage) Bounds() image.Rectangle {
  rect := image.Rectangle{image.Point{0,0},
                          image.Point{i.config.Width,i.config.Height}}
  return rect
}
 
func (i  MyColorImage) ColorModel() color.Model {
  return i.config.ColorModel
}
 
 
func (i  MyColorImage) At(x, y int) color.Color {
  return i.backColor
}
 
func main() {
  m :=  MakeColorImage(500, 300)
  pic.ShowImage(m)  // If you want to draw.
}

Threads ("Goroutines")

A goroutine is a lightweight thread managed by the Go runtime - go f(x, y, z) starts a new goroutine running f(x, y, z).

package main
import "fmt"
import "time"
 
func say(s string) {
  for i := 0; i < 5; i++ {
    time.Sleep(1000 * time.Millisecond)
    fmt.Println(s)
  }
}
 
func main() {
  go say("world")
  say("hello")
}

Channels

Channels are a typed conduit through which you can send and receive values with the channel operator, <-. I don't quite understand, but it sounds like a queue (fifo) to me.

package main
import "fmt"
 
func sum(a []int, c chan int) {
  sum := 0
  for _, v := range a {
    sum += v
  }
  c <- sum // send sum to c
}
 
func main() {
  a := []int{7, 2, 8, -9, 4, 0}
  c := make(chan int)         // Is how you setup channels.
  go sum(a[:len(a)/2], c)     // Sum first half array into c.
  go sum(a[len(a)/2:], c)     // Sum second half array into c.
  x, y := <-c, <-c            // Receive from c
  fmt.Println(x, y, x+y)      // Prints: 17 -5 12
}


package main
 
import "fmt"
 
func fibonacci(c, quit chan int) {
  x, y := 0, 1
  for {
    select {  // Select blocks until one of its cases can run!
      case c <- x:
        x, y = y, x+y
      case <-quit:
        fmt.Println("quit")
        return
    }
  }
}
 
func main() {
  c := make(chan int)
  quit := make(chan int)
  go func() {  // This goroutine will generate numbers (1-5) and each feeds immediately into "fibonacci"
    for i := 0; i < 5; i++ {
      fmt.Println(<-c)
    }
    quit <- 0
  }()
  fibonacci(c, quit)
}  // Prints: 0 | 1 | 1 | 2 | 3 | quit

Links