DEV Community

NMTechBytes | Anum Malik
NMTechBytes | Anum Malik

Posted on • Originally published at Medium

11 Solutions of Exercises in GoLang Tour

Alt Text

The Tour of Go Website is an excellent start for an developer trying to learn the language Go. It gradually introduces you to the language by explaining different parts and provides exercises to the reader to implement. 

Following are the solutions I wrote for the exercises as I went through the tour.

Exercise 1. Loops and Functions

Given a number x, we want to find the number z for which z² is most nearly x.

package main
import (
  "fmt"
  "math"
)
func Sqrt(x float64) float64 {
  z := float64(1)
  for i:=1; i<=10; i++ {
    fmt.Println(z)
    z -= (z * z - x) / (2 * z)
  }
  return z
}
func main() {
  fmt.Println(Sqrt(2))
  fmt.Println(math.Sqrt(2))
}
Enter fullscreen mode Exit fullscreen mode

Exercise 2. Slices

Implement Pic. It should return a slice of length dy, each element of which is a slice of dx 8-bit unsigned integers.

package main
import "golang.org/x/tour/pic"
func Pic(dx, dy int) [][]uint8 {
  var result = make([][]uint8, dy)
  for x := range result {
    result[x] = make([]uint8, dx)
    for y:= range result[x] {
      result[x][y] = uint8(x*y)
    }
  }
  return result
}
func main() {
  pic.Show(Pic)
}
Enter fullscreen mode Exit fullscreen mode

Exercise 3. Maps

Implement WordCount. It should return a map of the counts of each "word" in the string s. The wc. Test function runs a test suite against the provided function and prints success or failure.

package main
import (
 "golang.org/x/tour/wc"
 "strings"
)
func WordCount(s string) map[string]int {
  result := make(map[string]int)
  words := strings.Fields(s)
  for _, word := range words {
    result[word] += 1
  }
  return result
}
func main() {
  wc.Test(WordCount)
}
Enter fullscreen mode Exit fullscreen mode

Exercise 4. Fibonacci Closure

Implement a fibonacci function that returns a function (a closure) that returns successive fibonacci numbers (0, 1, 1, 2, 3, 5, …)

package main
import "fmt"
func fibonacci() func() int {
  total, nextTotal := 0, 1
  return func() int {
    result := total
    total, nextTotal = nextTotal, nextTotal + result
    return result
  }
}
func main() {
  f := fibonacci()
  for i := 0; i < 10; i++ {
    fmt.Println(f())
  }
}
Enter fullscreen mode Exit fullscreen mode

Exercise 5. Stringers

IPAddr type implement fmt.Stringer to print the address as a dotted quad.

package main
import "fmt"
type IPAddr [4]byte
func (ia IPAddr) String() string {
  return fmt.Sprintf("%d %d %d %d", ia[0], ia[1], ia[2], ia[3]) 
}
func main() {
  hosts := map[string]IPAddr{
    "loopback":  {127, 0, 0, 1},
    "googleDNS": {8, 8, 8, 8},
  }
  for name, ip := range hosts {
    fmt.Printf("%v: %v\n", name, ip)
  }
}
Enter fullscreen mode Exit fullscreen mode

Exercise 6. Errors

Sqrt should return a non-nil error value when given a negative number, as it doesn't support complex numbers.

package main
import (
 "fmt"
 "math"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
  return fmt.Sprintf("cannot Sqrt negative number: %d", e)
}
func Sqrt(x float64) (float64, error) {
  if (x < 0) {
    return 0, ErrNegativeSqrt(x) 
  }
  z := float64(1)
  tolerance := 1e-6

  for {
    oldz := z
    z -= (z * z - x) / (2 * z)
    if math.Abs(z-oldz) < tolerance {
      break; 
    }
  }
  return z, nil
}
func main() {
  fmt.Println(Sqrt(2))
  fmt.Println(Sqrt(-2))
}
Enter fullscreen mode Exit fullscreen mode

Exercise 7. Readers

Implement a Reader type that emits an infinite stream of the ASCII character 'A'.

package main
import "golang.org/x/tour/reader"
type MyReader struct{}
func (r MyReader) Read(b []byte) (int, error) {
  a := 'A'
  for i:=0; i < len(b); i++ {
    b[i] = a 
  }
  return len(b), nil
}
// TODO: Add a Read([]byte) (int, error) method to MyReader.
func main() {
  reader.Validate(MyReader{})
}
Enter fullscreen mode Exit fullscreen mode

Exercise 8. rot13Reader

Implement a rot13Reader that implements io.Reader and reads from an io.Reader, modifying the stream by applying the rot13 substitution cipher to all alphabetical characters

package main
import (
 "io"
 "os"
 "strings"
)
type rot13Reader struct {
  r io.Reader
}
func (rd *rot13Reader) Read(b []byte) (n int, e error) {
  n, e = rd.r.Read(b)
  for i:=0; i<len(b); i++ {
    c := b[i] 
    if (c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M')  {
      b[i] += 13;    
    } else if (c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z') {
      b[i]  -= 13;
    }
  } 
  return
}
func main() {
  s := strings.NewReader("Lbh penpxrq gur pbqr!")
  r := rot13Reader{s}
  io.Copy(os.Stdout, &r)
}
Enter fullscreen mode Exit fullscreen mode

Exercise 9: Images

Write another one, but this time it will return an implementation of image.Image instead of a slice of data. Define your own Image type, implement the necessary methods, and call pic.ShowImage. Bounds should return a image.Rectangle, like image.Rect(0, 0, w, h). ColorModel should return color.RGBAModel. At should return a color; the value v in the last picture generator corresponds to color.RGBA{v, v, 255, 255} in this one.

package main
import (
  "golang.org/x/tour/pic"
  "image"
  "image/color"
)
type Image struct{
  x int
  y int
}
func (i Image) Bounds() image.Rectangle {
  return image.Rect(0, 0, i.x, i.y)
}
func (i Image) At(x, y int) color.Color {
  v := uint8(x*y)
  return color.RGBA{v, v, 255, 255}
}
func (i Image) ColorModel() color.Model {
  return color.RGBAModel
}
func main() {
  m := Image{256, 65}
  pic.ShowImage(m)
}
Enter fullscreen mode Exit fullscreen mode

Exercise 10: Equivalent Binary Trees

Implement the Walk function and Same function using Walk to determine whether t1 and t2 store the same values.

package main
import (
 "golang.org/x/tour/tree"
 "fmt"
)
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
  WalkHelper(t, ch)
  close(ch)
}
func WalkHelper(t *tree.Tree, ch chan int) {
  if t == nil {
   return 
  }
  WalkHelper(t.Left, ch)
  ch <- t.Value
  WalkHelper(t.Right, ch)
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
  ch1 := make(chan int)
  ch2 := make(chan int)
  go Walk(t1, ch1)
  go Walk(t2, ch2)
  for v1 := range ch1 {
    v2 := <-ch2
    if v1 != v2 {
      return false 
    }
  }
  return true 
}
func main() {
  fmt.Println(Same(tree.New(1), tree.New(1))) 
  fmt.Println(Same(tree.New(1), tree.New(2))) 
}
Enter fullscreen mode Exit fullscreen mode

Exercise 11: Web Crawler

Use Go's concurrency features to parallelize a web crawler.Modify the Crawl function to fetch URLs in parallel without fetching the same URL twice.

package main
import (
  "fmt"
  "sync"
)
type Fetcher interface {
  // Fetch returns the body of URL and
  // a slice of URLs found on that page.
  Fetch(url string) (body string, urls []string, err error)
}
var cache = make(Cache)
var wg sync.WaitGroup
var mux sync.Mutex
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
  defer wg.Done()
  if cache.get(url) {
    fmt.Printf("xx Skipping: %s\n", url)
    return
  }
  fmt.Printf("** Crawling: %s\n", url)
  cache.set(url, true)

  if depth <= 0 {
    return
  }
  body, urls, err := fetcher.Fetch(url)
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Printf("found: %s %q\n", url, body)
  for _, u := range urls {
    wg.Add(1)
    go Crawl(u, depth-1, fetcher)
  }
  return
}
func main() {
  wg.Add(1)
  Crawl("https://golang.org/", 4, fetcher)
  wg.Wait()
}
type Cache map[string]bool
func (ch Cache) get(key string) bool {
  mux.Lock()
  defer mux.Unlock()
  return cache[key]
}
func (ch Cache) set(key string, value bool) {
  mux.Lock()
  defer mux.Unlock()
  cache[key] = value
}
// fakeFetcher is Fetcher that returns canned results.
type fakeFetcher map[string]*fakeResult
type fakeResult struct {
  body string
  urls []string
}
func (f fakeFetcher) Fetch(url string) (string, []string, error) {
  if res, ok := f[url]; ok {
    return res.body, res.urls, nil
  }
  return "", nil, fmt.Errorf("not found: %s", url)
}
// fetcher is a populated fakeFetcher.
var fetcher = fakeFetcher{
  "https://golang.org/": &fakeResult{
   "The Go Programming Language",
  []string{
   "https://golang.org/pkg/",
   "https://golang.org/cmd/",
  },
 },
 "https://golang.org/pkg/": &fakeResult{
  "Packages",
  []string{
   "https://golang.org/",
   "https://golang.org/cmd/",
   "https://golang.org/pkg/fmt/",
   "https://golang.org/pkg/os/",
  },
 },
 "https://golang.org/pkg/fmt/": &fakeResult{
  "Package fmt",
  []string{
   "https://golang.org/",
   "https://golang.org/pkg/",
  },
 },
 "https://golang.org/pkg/os/": &fakeResult{
  "Package os",
  []string{
   "https://golang.org/",
   "https://golang.org/pkg/",
  },
 },
}
Enter fullscreen mode Exit fullscreen mode

--

Anum Malik

Follow NMTechBytes on Twitter for my daily tech bites :)

Special Thanks …

Top comments (2)

Collapse
 
maxheiber profile image
Max Heiber

I think the solution for exercise 10 may be incorrect.

Example:

  right := tree.New(1)
  right.Right = tree.New(3)
  fmt.Println(Same(tree.New(1), right)) 
Enter fullscreen mode Exit fullscreen mode

Expected output: false

Actual output: true

Change suggestion:

Adding these lines above return true seems to fix the problem:

    for _ = range ch2 {
       return false 
    }
Enter fullscreen mode Exit fullscreen mode
Collapse
 
chris_e72b9146740ce6d25 profile image
Chris D

exercise 11
Just lock cache set or get is not good enough. You will need atomic read–modify–write so that no other thread can read between current thread cache get and set.