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)
}
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
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
// 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)
}
}