Skip to content

Instantly share code, notes, and snippets.

@Gabriel-Ladzaretti
Last active April 2, 2023 08:46
Show Gist options
  • Save Gabriel-Ladzaretti/93210e248f32036eb173d3487dc6a0e8 to your computer and use it in GitHub Desktop.
Save Gabriel-Ladzaretti/93210e248f32036eb173d3487dc6a0e8 to your computer and use it in GitHub Desktop.
A Tour of Go - solutions

Exercise: Loops and Functions

https://go.dev/tour/flowcontrol/8

package main

import (
	"fmt"
	"math"
)

func Sqrt(x float64) float64 {
	z := x / 2
	t := 0.0

	for math.Abs(z-t) > 1e-8 {
		t = z
		z -= (z*z - x) / (2 * z)
	}

	return z
}

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

Exercise: Slices

https://go.dev/tour/moretypes/18

package main

import "golang.org/x/tour/pic"

func Pic(dx, dy int) [][]uint8 {
	m := make([][]uint8, dy)

	for y := range m {
		m[y] = make([]uint8, dx)
		for x := range m[y] {
			m[y][x] = uint8(x * y)
		}
	}

	return m
}

func main() {
	pic.Show(Pic)
}

Exercise: Maps

https://go.dev/tour/moretypes/23

package main

import (
	"strings"

	"golang.org/x/tour/wc"
)

func WordCount(s string) map[string]int {
	m := make(map[string]int)
	for _, v := range strings.Fields(s) {
		_, ok := m[v]
		if !ok {
			m[v] = 0
		}
		m[v]++
	}
	return m
}

func main() {
	wc.Test(WordCount)
}

Exercise: Fibonacci closure

https://go.dev/tour/moretypes/26

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
	i, j := 0, 1
	return func() int {
		i, j = j, i+j
		return i
	}
}

func main() {
	f := fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(f())
	}
}

Exercise: Stringers

https://go.dev/tour/methods/18

package main

import (
	"fmt"
	"strconv"
	"strings"
)
type IPAddr [4]byte

func (ip IPAddr) String() string {
	s := make([]string, len(ip))
	for i, v := range ip {
		s[i] = strconv.Itoa(int(v))
	}
	return strings.Join(s, ".")
}

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

Exercise: Errors

https://go.dev/tour/methods/20

package main

import (
	"fmt"
	"math"
)

type ErrNegativeSqrt float64

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

func Sqrt(x float64) (float64, error) {
	if x < 0 {
		return x, ErrNegativeSqrt(x)
	}
	
	z := x / 2
	t := 0.0

	for math.Abs(z-t) > 1e-8 {
		t = z
		z -= (z*z - x) / (2 * z)
	}

	return z, nil
}

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

Exercise: Readers

https://go.dev/tour/methods/22

package main

import "golang.org/x/tour/reader"

type MyReader struct{}

// TODO: Add a Read([]byte) (int, error) method to MyReader.

func (r MyReader) Read(b []byte) (int, error) {
	i:= 0
	for i = range b {
		b[i] = 'A'
	}
	return i, nil
}


func main() {
	reader.Validate(MyReader{})
}

Exercise: rot13Reader

https://go.dev/tour/methods/23

package main

import (
	"io"
	"os"
	"strings"
)

type rot13Reader struct {
	r io.Reader
}

func (rot rot13Reader) Read(b []byte) (int, error) {
	d := make([]byte, cap(b))
	n, err := rot.r.Read(d)
	for i := 0; i < n; i++ {
		ch := int(d[i])
		if 'a' <= ch && ch < 'z' {
			ch = 'a' + ((ch-'a')+13)%26
		}
		if 'A' <= ch && ch < 'Z' {
			ch = 'A' + ((ch-'A')+13)%26
		}
		b[i] = byte(ch)
	}
	return n, err
}

func main() {
	s := strings.NewReader("Lbh penpxrq gur pbqr!")
	r := rot13Reader{s}
	io.Copy(os.Stdout, &r)
}

Exercise: Images

https://go.dev/tour/methods/25

package main

import (
	"image"
	"image/color"

	"golang.org/x/tour/pic"
)

type Image struct {
	dx, dy int
}

func (im Image) ColorModel() color.Model {
	return color.RGBAModel
}

func (im Image) Bounds() image.Rectangle {
	return image.Rect(0, 0, im.dx, im.dy)
}

func (im Image) At(x, y int) color.Color {
	return color.RGBA{uint8(x * y), uint8(x * y), 255, 255}
}

func main() {
	m := Image{255, 255}
	pic.ShowImage(m)
}

Generic types

https://go.dev/tour/generics/2

package main

import "fmt"

// Node represents a singly-linked list that holds
// values of any type.
type Node[T any] struct {
	next *Node[T]
	val  T
}

type List[T any] struct {
	root *Node[T]
}

func (n Node[T]) Traverse() {
	for cur := &n; cur != nil; {
		fmt.Printf("%v -> ", cur.val)
		cur = cur.next
	}
	fmt.Println("<nil>")
}

func (l List[T]) Print() {
	l.root.Traverse()
}

func insertAfter[T any](node *Node[T], newNode *Node[T]) {
	fmt.Println("insert node", newNode, "after node", node)
	newNode.next = node.next
	node.next = newNode
}

func removeAfter[T any](node *Node[T]) {
	fmt.Println("remove node", node.next)
	node.next = node.next.next
}

func insertRoot[T any](list *List[T], newNode *Node[T]) {
	fmt.Println("insert node", newNode, "as list root")
	newNode.next = list.root
	list.root = newNode
}

func removeRoot[T any](list *List[T]) {
	fmt.Println("remove root node", list.root)
	list.root = list.root.next
}

func main() {
	tail := Node[int]{nil, 30}
	node := Node[int]{&tail, 20}
	root := Node[int]{&node, 10}

	list := List[int]{&root}

	list.Print()

	insertAfter(&root, &Node[int]{nil, 15})

	list.Print()

	removeAfter(&root)

	list.Print()

	insertRoot(&list, &Node[int]{nil, 5})

	list.Print()

	removeRoot(&list)

	list.Print()
}
// 10 -> 20 -> 30 -> <nil>
// insert node &{<nil> 15} after node &{0xc000108020 10}
// 10 -> 15 -> 20 -> 30 -> <nil>
// remove node &{0xc000108020 15}
// 10 -> 20 -> 30 -> <nil>
// insert node &{<nil> 5} as list root
// 5 -> 10 -> 20 -> 30 -> <nil>
// remove root node &{0xc000108030 5}
// 10 -> 20 -> 30 -> <nil>

Exercise: Equivalent Binary Trees

https://go.dev/tour/concurrency/8

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) {
	defer close(ch)

	var dfs func(t *tree.Tree)
	dfs = func(t *tree.Tree) {
		if t == nil {
			return
		}
		dfs(t.Left)
		ch <- t.Value
		dfs(t.Right)
	}

	dfs(t)
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
	c1, c2 := make(chan int), make(chan int)
	
	go Walk(t1, c1)
	go Walk(t2, c2)
	
	for {
		v1, ok1 := <-c1
		v2, ok2 := <-c2

		if v1 != v2 || ok1 != ok2 {
			return false
		}

		if !ok1 {
			return true
		}
	}
}

func main() {
	fmt.Println(Same(tree.New(1), tree.New(2))) // false
	fmt.Println(Same(tree.New(1), tree.New(1))) // true
}

Exercise: Web Crawler

https://go.dev/tour/concurrency/10

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

type safeCache[T any] struct {
	mu sync.Mutex
	v  map[string]T
}

func (sc *safeCache[T]) get(key string) (T, bool) {
	sc.mu.Lock()
	defer sc.mu.Unlock()
	v, ok := sc.v[key]
	return v, ok
}

func (sc *safeCache[T]) set(key string, value T) {
	sc.mu.Lock()
	sc.v[key] = value
	sc.mu.Unlock()
}

// safeCrawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func safeCrawl(cache *safeCache[string], url string, depth int, fetcher Fetcher) {
	if depth <= 0 {
		return
	}

	v, ok := cache.get(url)

	if ok {
		fmt.Printf("cached url: %s %s\n", url, v)
		return
	}

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

	cache.set(url, body)

	fmt.Printf("found: %s %q\n", url, body)
	var wg sync.WaitGroup
	for _, u := range urls {
		wg.Add(1)
		go func(url string) {
			safeCrawl(cache, url, depth-1, fetcher)
			wg.Done()
		}(u)
	}
	wg.Wait()
	return
}

func Crawl(url string, depth int, fetcher Fetcher) {
	safeCache := safeCache[string]{v: make(map[string]string)}
	safeCrawl(&safeCache, url, depth, fetcher)
}

func main() {
	Crawl("https://golang.org/", 4, fetcher)
}

// 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/",
		},
	},
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment