Go Methods

 

Function

Function closures

Function closures Go functions may be closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is “bound” to the variables.

For example, the adder function returns a closure. Each closure is bound to its own sum variable.

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			// here pos and neg has its own sum variable
			pos(i),
			neg(-2*i),
		)
	}
}

The Fibonacci closure exercise is fun. Here is my initial answer:

func fibonacci() func() int {
	cur := 0
	nxt := 1
	return func() int {
		res := cur
		tmp := nxt
		nxt = cur + nxt
		cur = tmp
		return res
	}
}

But the following one from this SO is neat:

// fibonacci returns a function that returns
// successive fibonacci numbers from each
// successive call
func fibonacci() func() int {
    first, second := 0, 1
    return func() int {
        ret := first
        first, second = second, first+second
        return ret
    }
}

Method

// class
type Vertex struct {
	X, Y float64
}

type MyFloat float64

// method
// with parameter in between the func and method name
// can only declare with a receiver whose type is in the same package
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// method with pointer receiver
func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

// written as a regular function
func AbsFunc(v Vertex) float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// written as a regular function
func ScaleFunc(v *Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}


func main() {
	v := Vertex{3, 4}
    v.Scale(10) // this will change the value of V
	fmt.Println(v.Abs())
    ScaleFun(&v, 10) // use & for the type *T

    var v Vertex
    v.Scale(5)  // OK, even though v is a value, not a pointer
    // Go interprets the statement v.Scale(5) as (&v).Scale(5) since the Scale method has a pointer receiver.
	// The equivalent thing happens in the reverse direction.
    p := &v
    p.Scale(10) // OK
	ScaleFunc(p, 8) // OK

    fmt.Println(AbsFunc(v))
}

Choosing a value or pointer receiver

There are two reasons to use a pointer receiver.

The first is so that the method can modify the value that its receiver points to.

The second is to avoid copying the value on each method call. This can be more efficient if the receiver is a large struct, for example.

In this example, both Scale and Abs are with receiver type *Vertex, even though the Abs method needn’t modify its receiver.

In general, all methods on a given type should have either value or pointer receivers, but not a mixture of both. (We’ll see why over the next few pages.)

Interface

An interface type is defined as a set of method signatures.

A value of interface type can hold any value that implements those methods.

Interfaces are implemented implicitly A type implements an interface by implementing its methods. There is no explicit declaration of intent, no “implements” keyword.

Implicit interfaces decouple the definition of an interface from its implementation, which could then appear in any package without prearrangement.

Under the hood, interface values can be thought of as a tuple of a value and a concrete type:

(value, type) An interface value holds a value of a specific underlying concrete type.

Calling a method on an interface value executes the method of the same name on its underlying type.

Note both value and reference can implement an interface.

type I interface {
	M()
}

type T struct {
	S string
}

func (t *T) M() {
	fmt.Println(t.S)
}

type F float64

func (f F) M() {
	fmt.Println(f)
}

func main() {
	var i I

	i = &T{"Hello"}
	describe(i)
	i.M()

	i = F(math.Pi)
	describe(i)
	i.M()
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}

Another interface example

Error

Error interface

type error interface {
    Error() string
}

Handle Error

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() {
	// Functions often return an error value, and calling code should handle errors by testing whether the error equals nil.
	if err := run(); err != nil {
		fmt.Println(err)
	}
}

Error exercise

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}

func Sqrt(x float64) (float64, error) {
	if (x >= 0) {
		return x, nil
	} else {
		return x, ErrNegativeSqrt(x)
	}
}

func main() {
	fmt.Println(Sqrt(2))
	fmt.Println(Sqrt(-2))
}

Reader

rot13Reader

type rot13Reader struct {
	r io.Reader
}

func (r13 rot13Reader) Read(byteArray []byte) (int, error) {
    dict := make(map[byte]byte)
    input := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    output := "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"
    for idx := range input {
        dict[input[idx]] = output[idx]
    }

    n, err := r13.r.Read(byteArray)
    for i := 0; i < n; i++ {
        if val, ok := dict[byteArray[i]]; ok {
          byteArray[i] = val
        }
    }
    return n, err
}

Concurrency

Exercise: Equivalent Binary Trees

reference

func Walk(t *tree.Tree, ch chan int) {
    defer close(ch) // <- closes the channel when this function returns
    var walk func(t *tree.Tree)
    walk = func(t *tree.Tree) {
        if t == nil {
            return
        }
        walk(t.Left)
        ch <- t.Value
        walk(t.Right)
    }
    walk(t)
}

Exercise: Web Crawler

reference

// SafeUrlMap is safe to use concurrently.
type SafeUrlMap struct {
    v   map[string]string
    mux sync.Mutex
}

func (c *SafeUrlMap) Set(key string, body string) {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    c.v[key] = body
    c.mux.Unlock()
}

// Value returns mapped value for the given key.
func (c *SafeUrlMap) Value(key string) (string, bool) {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    defer c.mux.Unlock()
    val, ok := c.v[key]
    return val, ok
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher, urlMap SafeUrlMap) {
    defer wg.Done()
    urlMap.Set(url, body)

    if depth <= 0 {
        return
    }

    body, urls, err := fetcher.Fetch(url)
    if err != nil {
        fmt.Println(err)
        return
    }

    for _, u := range urls {
        if _, ok := urlMap.Value(u); !ok {
            wg.Add(1)
            go Crawl(u, depth-1, fetcher, urlMap)
        }
    }

    return
}

var wg sync.WaitGroup

func main() {
    urlMap := SafeUrlMap{v: make(map[string]string)}

    wg.Add(1)
    go Crawl("http://golang.org/", 4, fetcher, urlMap)
    wg.Wait()

    for url := range urlMap.v {
        body, _ := urlMap.Value(url)
        fmt.Printf("found: %s %q\n", url, body)
    }
}