Exercises
- Exercises from the "A Tour of Go" Golang intro
Exercise: Loops and Functions
package main
import (
"fmt"
"math"
)
func Sqrt(x float64) float64 {
z := float64(x*x)
for i:=0; i<10; i++{
diff := (z*z-x) / (2*z)
z = z-diff
if diff<0.000000001{
break
}
}
return z
}
func main() {
in := 10.0
fmt.Println("Methods diverge by:", Sqrt(in)- math.Sqrt(in))
}
Exercise:Slices
Implement Pic. It should return a slice of length dy, each element of which is a slice of dx 8-bit unsigned integers. When you run the program, it will display your picture, interpreting the integers as grayscale (well, bluescale) values.
The choice of image is up to you. Interesting functions include (x+y)/2, x*y, and x^y.
(You need to use a loop to allocate each []uint8 inside the [][]uint8.)
(Use uint8(intValue) to convert between types.
package main
import (
"golang.org/x/tour/pic"
)
// makes a dx by dy large field of 8 bit unsigned slices to "draw" a picture.
func Pic(dx, dy int) [][]uint8 {
var p [][]uint8 = make([][]uint8, dy)
//allocate al []unit8s:
for i := 0; i < dy; i++ {
p[i] = make([]uint8, dx)
}
for x := 0; x < dx; x++ {
for y := 0; y < dy; y++ {
// p[x][y] = uint8((x+y)/2) // gradient towards corner
// p[x][y] = uint8(x*y) // fractals
gradX := (x-255) // cross-"ish":
if x<dx/2{gradX=255-x}
gradY := (y-255)
if y<dy/2{gradY=255-y}
p[x][y] = uint8(gradY+gradX)
}
}
return p
}
func main() {
pic.Show(Pic)
}
Exercise: 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.
You might find strings.Fields helpful.
package main
import (
"golang.org/x/tour/wc"
"strings"
)
func WordCount(s string) map[string]int {
m := make( map[string] int)
words := strings.Fields(s)
for _, word := range words {
_, ok := m[word]
if ok { //if same word more than twice
m[word] ++
} else{
m[word] = 1
}
}
return m
}
func main() {
wc.Test(WordCount)
}
Exercise: Fibonacci closure
Let's have some fun with functions.
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"
// fibonacci is a function that returns successive fibonacci numbers 0 1 1 2 3 5 8 ...
func fibonacci() func() int {
prevN := 0
nextN := 1
return func() int{
// edge case for the start needed:
if prevN == 0 {
prevN = 1
return 0
}
prevN, nextN = nextN, prevN+nextN
return prevN
}
}
func main() {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
}
Exercise: 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{}
// TODO: Add a Read([]byte) (int, error) method to MyReader.
func (s MyReader) Read(b []byte) (int, error) {
// fill all (8) bit with A's
for idx := range b {
b[idx] = byte('A') // 'A' is the Char. String wouldnt work like this
// "A"[0] would also work reading the first byte of the string "A"
}
return len(b), nil
}
// :TODO
func main() {
reader.Validate(MyReader{})
}
Exercise: rot13Reader
A common pattern is an io.Reader that wraps another io.Reader, modifying the stream in some way.
For example, the gzip.NewReader function takes an io.Reader (a stream of compressed data) and returns a *gzip.Reader that also implements io.Reader (a stream of the decompressed data).
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.
The rot13Reader type is provided for you. Make it an io.Reader by implementing its Read method.
package main
import (
"io"
"os"
"strings"
)
type rot13Reader struct {
r io.Reader
}
func (reader rot13Reader) Read(b []byte) (int, error) {
n, err := reader.r.Read(b)
for i, val := range b[:n] {
if val >= 'a' && val <= 'z' {
b[i] = (val-'a'+13)%26 + 'a'
} else if val >= 'A' && val <= 'Z' {
b[i] = (val-'A'+13)%26 + 'A'
}
}
return n, err
}
func main() {
s := strings.NewReader("Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
Exercise: Images
Remember the picture generator you wrote earlier? Let's 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{}
// ColorModel returns the Image's color model.
func (i Image) ColorModel() color.Model {
return color.RGBAModel
}
// Bounds returns the domain for which At can return non-zero color.
// The bounds do not necessarily contain the point (0, 0).
func (i Image) Bounds() image.Rectangle {
return image.Rect(0, 0, 255, 255)
}
// At returns the color of the pixel at (x, y).
// At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid.
// At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one.
func (i Image) At(x, y int) color.Color {
return color.RGBA{uint8(x), uint8(y), 100, 255}
}
func main() {
m := Image{}
pic.ShowImage(m)
}
Exercise Equivalent Binary Trees
A function to check whether two binary trees store the same sequence is quite complex in most languages. We'll use Go's concurrency and channels to write a simple solution.
This example uses the tree package, which defines the type:
type Tree struct {
Left *Tree
Value int
Right *Tree
}
Exercise: Equivalent Binary Trees
Implement the Walk function.
Test the Walk function.
The function tree.New(k) constructs a randomly-structured (but always sorted) binary tree holding the values k, 2k, 3k, ..., 10k.
Create a new channel ch and kick off the walker:
go Walk(tree.New(1), ch) Then read and print 10 values from the channel. It should be the numbers 1, 2, 3, ..., 10.
Implement the Same function using Walk to determine whether t1 and t2 store the same values.
Test the Same function.
Same(tree.New(1), tree.New(1)) should return true, and Same(tree.New(1), tree.New(2)) should return false.
The documentation for Tree can be found here.
package main
import (
"fmt"
"golang.org/x/tour/tree"
)
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
innerWalk(t, ch)
close(ch)
}
func innerWalk(t *tree.Tree, ch chan int) {
if t.Left != nil {
innerWalk(t.Left, ch)
}
ch <- t.Value
if t.Right != nil {
innerWalk(t.Right, ch)
}
}
// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
chan1 := make(chan int)
chan2 := make(chan int)
go Walk(t1, chan1)
go Walk(t2, chan2)
for val1 := range chan1{
val2 := <-chan2
if val1 != val2 {
return false
}
}
return true
}
func main() {
fmt.Println( Same(tree.New(1), tree.New(1)) )
fmt.Println( Same(tree.New(1), tree.New(20)) )
}
Exercise: Web Crawler
In this exercise you'll 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.
Hint: you can keep a cache of the URLs that have been fetched on a map, but maps alone are not safe for concurrent use!
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)
}
// create a mutex to store data savely.
type SafeMap struct{
mut sync.Mutex
data map[string]string
}
// write to data without anyone else accessing data simultaneously
func (mapp *SafeMap) addLink(url string, body string){
mapp.mut.Lock()
mapp.data[url] = body
defer mapp.mut.Unlock()
}
/*
// read from data without anyone else accessing data simultaneously.
func (mapp *SafeMap) readSavely(key string) (string, bool) {
mapp.mut.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer mapp.mut.Unlock()
val, ok := mapp.data[key]
return val, ok
}
*/
// crawls the url, getting all urls, going deeper till it reaches certain depth.
// adds new entries into saveMap.data and prints out errors
func Crawl(url string, depth int, fetcher Fetcher, saveMap SafeMap) {
defer wg.Done()
if depth <= 0 {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
saveMap.addLink(url, body)
for _, url := range urls {
_, ok := saveMap.data[url]; // we could lock data while reading with saveMap.readSavely(url)
if !ok{ // but i dont think it should be a neccessary for just this usecase.
wg.Add(1)
go Crawl(url, depth-1, fetcher, saveMap)
}
}
return
}
var wg sync.WaitGroup // CANT use this inside main() and pass it on(actually maybe as a pointer) it HAS TO BE GLOBAL!
func main() {
wg.Add(1) // we add() count+=1 every time we create a Crawl() instance
links := SafeMap{data: make(map[string]string)} // and on defer wg.Done(), once that crawl finishes: counts-=1
Crawl("https://golang.org/", 4, fetcher, links)
wg.Wait() // Once total reaches 0 we know every process finished and we move on with main()
// print out the stored data:
for url, body := range links.data{
fmt.Println("Url:", url," -> Body:", body)
}
}
// 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...<html>...",
[]string{
"https://golang.org/pkg/",
"https://golang.org/cmd/",
},
},
"https://golang.org/pkg/": &fakeResult{
"Packages...<html>...",
[]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...<html>...",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
"https://golang.org/pkg/os/": &fakeResult{
"Package os...<html>...",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
}