├── .gitignore ├── 99bottles └── main.go ├── LICENSE ├── cat ├── README.md └── main.go ├── chat ├── README.md └── main.go ├── dbtest ├── README.md └── main.go ├── fib ├── README.md └── main.go ├── findgo ├── README.md ├── bar │ ├── 123 │ ├── 456 │ └── delta │ │ └── a.go ├── foo │ ├── a │ ├── b │ └── beta │ │ └── b.go └── main.go ├── fizzbuzz └── main.go ├── fordefer └── main.go ├── kvstore ├── README.md └── main.go ├── mapops ├── README.md └── main.go ├── proxyhttp ├── README.md └── main.go ├── queryspeed ├── README.md └── main.go ├── rgrep ├── README.md └── main.go ├── runners ├── README.md ├── main.go └── main_test.go ├── slices └── main.go ├── statemachine ├── README.md ├── main.go ├── sm.dot └── sm.png └── wordcount ├── README.md └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /99bottles/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | for i := 99; i > 0; i-- { 5 | println(i, "bottles of beer on the wall") 6 | println(i, "bottles of beer") 7 | println("take one down, pass it around...") 8 | } 9 | println("we're all done!") 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Peter Bourgon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cat/README.md: -------------------------------------------------------------------------------- 1 | # cat 2 | 3 | Write a program called cat that takes a filename as input and writes the 4 | content to os.Stdout. 5 | 6 | Bonus: do it without buffering the file contents in memory. 7 | 8 | Bonus: add a command line flag to change the output to os.Stderr. Hint: see 9 | package flag. 10 | 11 | Bonus: add a command line flag to optionally print the size of the file at the 12 | end of the output. 13 | -------------------------------------------------------------------------------- /cat/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | var ( 12 | stderr = flag.Bool("stderr", false, "output to stderr instead of stdout") 13 | sz = flag.Bool("sz", false, "print size at end") 14 | ) 15 | flag.Parse() 16 | 17 | if flag.NArg() != 1 { 18 | fmt.Fprintf(os.Stderr, "usage: %s [options] \n", os.Args[0]) 19 | os.Exit(1) 20 | } 21 | 22 | f, err := os.Open(flag.Arg(0)) 23 | if err != nil { 24 | fmt.Fprintf(os.Stderr, "%v\n", err) 25 | os.Exit(2) 26 | } 27 | 28 | dst := os.Stdout 29 | if *stderr { 30 | dst = os.Stderr 31 | } 32 | 33 | n, err := io.Copy(dst, f) 34 | if err != nil { 35 | fmt.Fprintf(os.Stderr, "%v\n", err) 36 | os.Exit(3) 37 | } 38 | 39 | if *sz { 40 | fmt.Fprintf(dst, "%d\n", n) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /chat/README.md: -------------------------------------------------------------------------------- 1 | # chat 2 | 3 | Write a program called chat that can be started in two modes. 4 | 5 | In server mode, it should accept TCP connections on a user-specified host:port, 6 | and broadcast all received messages to all connected clients. Prefix broadcast 7 | messages with origin address. 8 | 9 | In client mode, it should connect to a user-specified host:port on TCP, submit 10 | messages from stdin, and write received messages to stdout. 11 | 12 | -------------------------------------------------------------------------------- /chat/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net" 9 | "os" 10 | ) 11 | 12 | // Based on The Go Programming Language chapter 8 "chat" example. 13 | // https://github.com/adonovan/gopl.io/blob/master/ch8/chat/chat.go 14 | 15 | func main() { 16 | var ( 17 | server = flag.Bool("server", false, "server mode") 18 | client = flag.Bool("client", false, "client mode") 19 | addr = flag.String("addr", "127.0.0.1:5000", "chat server address") 20 | ) 21 | flag.Parse() 22 | 23 | switch { 24 | case *server && *client: 25 | log.Fatal("can't be both server and client") 26 | case *server && !*client: 27 | runServer(*addr) 28 | case !*server && *client: 29 | runClient(*addr) 30 | case !*server && !*client: 31 | log.Fatal("must be either server or client") 32 | } 33 | } 34 | 35 | // To the server, a client is a pipe that needs to receive messages. 36 | type client chan<- string 37 | 38 | func runServer(addr string) { 39 | // Bind listener 40 | ln, err := net.Listen("tcp", addr) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | log.Printf("server: listening on %s", ln.Addr().String()) 46 | 47 | // Client lifecycle management 48 | var ( 49 | hello = make(chan client) // client connects 50 | goodbye = make(chan client) // client disconnects 51 | broadcast = make(chan string) // messages to all clients 52 | ) 53 | 54 | // The goroutine to send broadcasts. 55 | go func() { 56 | clients := map[client]struct{}{} 57 | for { 58 | select { 59 | case client := <-hello: 60 | clients[client] = struct{}{} 61 | log.Printf("hello: now %d client(s)", len(clients)) 62 | 63 | case client := <-goodbye: 64 | delete(clients, client) 65 | close(client) 66 | log.Printf("goodbye: now %d client(s)", len(clients)) 67 | 68 | case message := <-broadcast: 69 | log.Printf("broadcast: to %d client(s)", len(clients)) 70 | for client := range clients { 71 | client <- message 72 | } 73 | } 74 | } 75 | }() 76 | 77 | for { 78 | conn, err := ln.Accept() 79 | if err != nil { 80 | log.Print(err) 81 | continue 82 | } 83 | 84 | go handleClient(conn, hello, goodbye, broadcast) 85 | } 86 | } 87 | 88 | func handleClient(conn net.Conn, hello, goodbye chan<- client, toBroadcast chan<- string) { 89 | // Lifecycle management. 90 | toClient := make(chan string) 91 | hello <- toClient 92 | defer func() { goodbye <- toClient }() 93 | 94 | // Announce the presence of a new chatter. 95 | handle := conn.RemoteAddr().String() 96 | toBroadcast <- fmt.Sprintf("%s has joined the chat", handle) 97 | defer func() { toBroadcast <- fmt.Sprintf("%s has left the chat", handle) }() 98 | 99 | // Messages from the chat server to the conn. 100 | // We range over the toClient channel and exit when it's closed. 101 | // (It's closed after it's processed by the goodbye handler.) 102 | go func() { 103 | for message := range toClient { 104 | fmt.Fprintf(conn, message+"\n") 105 | } 106 | }() 107 | 108 | // Messages from the conn to the chat server. 109 | // When the client disconnects, the scanner stops scanning. 110 | // We close the conn, and signal our goodbye via the defer. 111 | s := bufio.NewScanner(conn) 112 | for s.Scan() { 113 | toBroadcast <- "<" + handle + "> " + s.Text() 114 | } 115 | } 116 | 117 | func runClient(addr string) { 118 | conn, err := net.Dial("tcp", addr) 119 | if err != nil { 120 | log.Fatal(err) 121 | } 122 | 123 | defer conn.Close() 124 | 125 | // Messages from the conn to stdout. 126 | // The scanner will exit when the conn is closed. 127 | // We close the conn via the defer. 128 | go func() { 129 | s := bufio.NewScanner(conn) 130 | for s.Scan() { 131 | fmt.Fprintf(os.Stdout, s.Text()+"\n") 132 | } 133 | }() 134 | 135 | // Messages from stdin to the conn. 136 | // The scanner will exit when stdin is closed. 137 | // We close stdin via ctrl-D. 138 | s := bufio.NewScanner(os.Stdin) 139 | for s.Scan() { 140 | fmt.Fprintf(conn, s.Text()+"\n") 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /dbtest/README.md: -------------------------------------------------------------------------------- 1 | # dbtest 2 | 3 | Write a program called dbtest that uses database/sql to insert, select, and 4 | delete records from a SQLite database. Hint: you will need to use a third-party 5 | SQLite driver, github.com/mattn/go-sqlite3. 6 | 7 | -------------------------------------------------------------------------------- /dbtest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | 7 | _ "github.com/mattn/go-sqlite3" 8 | ) 9 | 10 | func main() { 11 | db, err := sql.Open("sqlite3", "db.sqlite") 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | if _, err = db.Exec("CREATE TABLE foo (name VARCHAR, age INT);"); err != nil { 17 | panic(err) 18 | } 19 | fmt.Printf("CREATE OK\n") 20 | 21 | res, err := db.Exec(`INSERT INTO foo (name, age) VALUES (?, ?);`, "Alice", 40) 22 | if err != nil { 23 | panic(err) 24 | } 25 | n, err := res.RowsAffected() 26 | if err != nil { 27 | panic(err) 28 | } 29 | fmt.Printf("INSERT: %d row(s) OK\n", n) 30 | 31 | rows, err := db.Query("SELECT name, age FROM foo WHERE name = ?", "Alice") 32 | if err != nil { 33 | panic(err) 34 | } 35 | var name string 36 | var age int 37 | for rows.Next() { 38 | if err := rows.Scan(&name, &age); err != nil { 39 | panic(err) 40 | } 41 | fmt.Printf("SELECT: %q %d\n", name, age) 42 | } 43 | 44 | res, err = db.Exec(`DELETE FROM foo WHERE name = ?`, "Alice") 45 | if err != nil { 46 | panic(err) 47 | } 48 | n, err = res.RowsAffected() 49 | if err != nil { 50 | panic(err) 51 | } 52 | fmt.Printf("DELETE: %d row(s) OK\n", n) 53 | } 54 | -------------------------------------------------------------------------------- /fib/README.md: -------------------------------------------------------------------------------- 1 | # fib 2 | 3 | ```go 4 | func fibonacci(n int, c chan int) { 5 | // Implement this function 6 | } 7 | 8 | func main() { 9 | c := make(chan int) 10 | go fibonacci(10, c) 11 | for i := range c { 12 | println(i) 13 | } 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /fib/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func fibonacci(n int, c chan int) { 4 | a, b := 1, 1 5 | for i := 0; i < n; i++ { 6 | c <- a 7 | a, b = b, a+b 8 | } 9 | close(c) 10 | } 11 | 12 | func main() { 13 | c := make(chan int) 14 | go fibonacci(10, c) 15 | for i := range c { 16 | println(i) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /findgo/README.md: -------------------------------------------------------------------------------- 1 | # findgo 2 | 3 | Write a program called findgo that prints the absolute path of the .go files in 4 | the current directory and all subdirectories recursively. Hint: filepath.Walk, 5 | filepath.Ext, filepath.Abs. 6 | -------------------------------------------------------------------------------- /findgo/bar/123: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterbourgon/go-training/ae312cb6892b3edcda2b4312bdb3783a9cd283ea/findgo/bar/123 -------------------------------------------------------------------------------- /findgo/bar/456: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterbourgon/go-training/ae312cb6892b3edcda2b4312bdb3783a9cd283ea/findgo/bar/456 -------------------------------------------------------------------------------- /findgo/bar/delta/a.go: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterbourgon/go-training/ae312cb6892b3edcda2b4312bdb3783a9cd283ea/findgo/bar/delta/a.go -------------------------------------------------------------------------------- /findgo/foo/a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterbourgon/go-training/ae312cb6892b3edcda2b4312bdb3783a9cd283ea/findgo/foo/a -------------------------------------------------------------------------------- /findgo/foo/b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterbourgon/go-training/ae312cb6892b3edcda2b4312bdb3783a9cd283ea/findgo/foo/b -------------------------------------------------------------------------------- /findgo/foo/beta/b.go: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterbourgon/go-training/ae312cb6892b3edcda2b4312bdb3783a9cd283ea/findgo/foo/beta/b.go -------------------------------------------------------------------------------- /findgo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func main() { 10 | filepath.Walk(".", func(path string, info os.FileInfo, err error) error { 11 | if filepath.Ext(path) == ".go" { 12 | abs, err := filepath.Abs(path) 13 | if err != nil { 14 | panic(err) 15 | } 16 | fmt.Printf("%s\n", abs) 17 | } 18 | return nil 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /fizzbuzz/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | withIf() 5 | // withSwitch() 6 | } 7 | 8 | func withIf() { 9 | for i := 1; i <= 100; i++ { 10 | if i%3 == 0 && i%5 == 0 { 11 | println("FizzBuzz") 12 | } else if i%3 == 0 { 13 | println("Fizz") 14 | } else if i%5 == 0 { 15 | println("Buzz") 16 | } else { 17 | println(i) 18 | } 19 | } 20 | } 21 | 22 | func withSwitch() { 23 | for i := 1; i <= 100; i++ { 24 | switch { 25 | case i%3 == 0 && i%5 == 0: 26 | println("FizzBuzz") 27 | case i%3 == 0: 28 | println("Fizz") 29 | case i%5 == 0: 30 | println("Buzz") 31 | default: 32 | println(i) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /fordefer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | for i := 0; i < 3; i++ { 5 | func() { 6 | println("hello", i) 7 | defer println("goodbye", i) 8 | }() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /kvstore/README.md: -------------------------------------------------------------------------------- 1 | # kvstore 2 | 3 | Create an in-memory key/value store. Keys and values are strings. Implement 4 | basic operations: insert, update, upsert, contains, delete.

Bonus: implement 5 | an iterator operation. Hint: the user should pass a function that will be 6 | called for every key/value in the store. 7 | -------------------------------------------------------------------------------- /kvstore/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | s := newStore() 5 | s.insert("a", "1") 6 | s.update("a", "2") 7 | s.upsert("b", "3") 8 | println(s.contains("a")) 9 | s.delete("a") 10 | println(s.contains("a")) 11 | s.insert("c", "c") 12 | s.insert("3", "x") 13 | s.iterate(func(k, v string) error { 14 | println("iterate:", k, v) 15 | return nil 16 | }) 17 | } 18 | 19 | type store struct { 20 | m map[string]string 21 | } 22 | 23 | func newStore() *store { 24 | return &store{ 25 | m: map[string]string{}, 26 | } 27 | } 28 | 29 | func (s *store) insert(k, v string) { 30 | if _, ok := s.m[k]; !ok { 31 | s.m[k] = v 32 | } 33 | } 34 | 35 | func (s *store) update(k, v string) { 36 | if _, ok := s.m[k]; ok { 37 | s.m[k] = v 38 | } 39 | } 40 | 41 | func (s *store) upsert(k, v string) { 42 | s.m[k] = v 43 | } 44 | 45 | func (s *store) contains(k string) bool { 46 | _, ok := s.m[k] 47 | return ok 48 | } 49 | 50 | func (s *store) delete(k string) { 51 | delete(s.m, k) 52 | } 53 | 54 | func (s *store) iterate(fn func(k, v string) error) { 55 | for k, v := range s.m { 56 | if err := fn(k, v); err != nil { 57 | return 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /mapops/README.md: -------------------------------------------------------------------------------- 1 | # mapops 2 | 3 | Create a map of integers (1..100) to their squares.
Compute the sum of squares 4 | of even numbers.
Compute the sum of squares of odd numbers. 5 | -------------------------------------------------------------------------------- /mapops/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | m := map[int]int{} 5 | for i := 1; i <= 100; i++ { 6 | m[i] = i * i 7 | } 8 | 9 | var sumSquareEven, sumSquareOdd int 10 | for k, v := range m { 11 | if k%2 == 0 { 12 | sumSquareEven += v 13 | } else { 14 | sumSquareOdd += v 15 | } 16 | } 17 | 18 | println("sum of squares of even numbers:", sumSquareEven) 19 | println("sum of squares of odd numbers:", sumSquareOdd) 20 | } 21 | -------------------------------------------------------------------------------- /proxyhttp/README.md: -------------------------------------------------------------------------------- 1 | # proxyhttp 2 | 3 | Write a program called proxyhttp that implements an HTTP server that proxies 4 | all requests to google.com. 5 | 6 | Hint: you will need to construct a new HTTP request from the request you get in 7 | the server's handler. 8 | 9 | Bonus: make the proxy target a command line flag. 10 | 11 | -------------------------------------------------------------------------------- /proxyhttp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "io" 6 | "log" 7 | "net/http" 8 | ) 9 | 10 | func main() { 11 | var ( 12 | listen = flag.String("listen", ":8080", "HTTP listen address") 13 | target = flag.String("target", "google.com", "host to proxy to") 14 | ) 15 | flag.Parse() 16 | 17 | http.HandleFunc("/", h(*target)) 18 | log.Printf("listening on %s", *listen) 19 | http.ListenAndServe(*listen, nil) 20 | } 21 | 22 | func h(target string) http.HandlerFunc { 23 | return func(w http.ResponseWriter, r *http.Request) { 24 | req, err := http.NewRequest(r.Method, r.URL.String(), r.Body) 25 | if err != nil { 26 | http.Error(w, err.Error(), http.StatusInternalServerError) 27 | return 28 | } 29 | req.URL.Host = target 30 | if req.URL.Scheme == "" { 31 | req.URL.Scheme = "http" 32 | } 33 | log.Printf("%s => %s", r.URL, req.URL) 34 | 35 | resp, err := http.DefaultClient.Do(req) 36 | if err != nil { 37 | http.Error(w, err.Error(), http.StatusInternalServerError) 38 | return 39 | } 40 | 41 | defer resp.Body.Close() 42 | io.Copy(w, resp.Body) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /queryspeed/README.md: -------------------------------------------------------------------------------- 1 | # queryspeed 2 | 3 | Write a program called queryspeed that takes a list of URLs as command line 4 | arguments, and for each one, concurrently performs an HTTP GET and reports the 5 | status code and time taken (in milliseconds). 6 | 7 | Hint: the program should take no longer than the slowest individual request. 8 | 9 | -------------------------------------------------------------------------------- /queryspeed/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | c := make(chan result, len(os.Args[1:])) 12 | for _, arg := range os.Args[1:] { 13 | go func(url string) { c <- get(url) }(arg) 14 | } 15 | for i := 0; i < cap(c); i++ { 16 | r := <-c 17 | fmt.Printf("%s: took %s, code %d, err %v\n", r.url, r.took, r.code, r.err) 18 | } 19 | } 20 | 21 | type result struct { 22 | url string 23 | took time.Duration 24 | code int 25 | err error 26 | } 27 | 28 | func get(url string) result { 29 | begin := time.Now() 30 | resp, err := http.Get(url) 31 | code := 0 32 | if err == nil { 33 | code = resp.StatusCode 34 | resp.Body.Close() 35 | } 36 | return result{ 37 | url: url, 38 | took: time.Since(begin), 39 | code: code, 40 | err: err, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rgrep/README.md: -------------------------------------------------------------------------------- 1 | # rgrep 2 | 3 | Write a program called rgrep that takes a regex as an argument, reads lines 4 | from stdin, and prints the lines that match the regex to stdout. Hint: 5 | regexp.Compile, bufio.NewScanner. 6 | -------------------------------------------------------------------------------- /rgrep/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | ) 9 | 10 | func main() { 11 | re, err := regexp.Compile(os.Args[1]) 12 | if err != nil { 13 | fmt.Printf("%v\n", err) 14 | os.Exit(1) 15 | } 16 | 17 | s := bufio.NewScanner(os.Stdin) 18 | for s.Scan() { 19 | if line := s.Text(); re.MatchString(line) { 20 | fmt.Printf("%s\n", line) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /runners/README.md: -------------------------------------------------------------------------------- 1 | # runners 2 | 3 | Given the runner interface, implement func race, which takes a distance and 4 | multiple runners, and calculates the winner based on the shortest time. Then, 5 | implement several runners. 6 | 7 | ```go 8 | type runner interface { 9 | name() string 10 | run(distance int) (seconds int) 11 | } 12 | 13 | func race( ??? ) (winner string) { 14 | // ??? 15 | } 16 | ``` 17 | 18 | Be creative. Some runners might run at a fixed speed; some might slow down with 19 | the distance; some might cheat and teleport? 20 | 21 | -------------------------------------------------------------------------------- /runners/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type runner interface { 4 | name() string 5 | run(distance int) (seconds int) 6 | } 7 | 8 | func race(distance int, runners ...runner) (winner string) { 9 | var fastest int 10 | for i, runner := range runners { 11 | took := runner.run(distance) 12 | println(runner.name(), "ran", distance, "meters in", took, "seconds") 13 | if i == 0 || took < fastest { 14 | fastest = took 15 | winner = runner.name() 16 | } 17 | } 18 | return winner 19 | } 20 | 21 | func main() { 22 | var ( 23 | r1 = baby{} 24 | r2 = triathlete{} 25 | r3 = robot{} 26 | ) 27 | winner := race(100, r1, r2, r3) 28 | println(winner, "won the race") 29 | } 30 | 31 | type baby struct{} 32 | 33 | func (baby) name() string { 34 | return "a baby" 35 | } 36 | 37 | func (baby) run(distance int) int { 38 | return distance * 30 // slow and steady 39 | } 40 | 41 | type triathlete struct{} 42 | 43 | func (triathlete) name() string { 44 | return "a triathlete" 45 | } 46 | 47 | func (triathlete) run(distance int) int { 48 | var took int // so far 49 | secondsPerMeter := 1 // initially fast 50 | for distance > 0 { 51 | took += secondsPerMeter 52 | secondsPerMeter++ // progressively slower 53 | distance-- 54 | } 55 | return took 56 | } 57 | 58 | type robot struct{} 59 | 60 | func (robot) name() string { 61 | return "RunBot 2000" 62 | } 63 | 64 | func (robot) run(distance int) int { 65 | return distance * 5 // pretty quick 66 | } 67 | -------------------------------------------------------------------------------- /runners/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestBaby(t *testing.T) { 6 | for distance, want := range map[int]int{ 7 | 1: 30 * 1, 8 | 2: 30 * 2, 9 | 3: 30 * 3, 10 | } { 11 | have := baby{}.run(distance) 12 | if want != have { 13 | t.Errorf("run(%d): want %d, have %d", distance, want, have) 14 | } 15 | } 16 | } 17 | 18 | func TestTriathlete(t *testing.T) { 19 | for distance, want := range map[int]int{ 20 | 1: 1, 21 | 2: 1 + 2, 22 | 3: 1 + 2 + 3, 23 | } { 24 | have := triathlete{}.run(distance) 25 | if want != have { 26 | t.Errorf("run(%d): want %d, have %d", distance, want, have) 27 | } 28 | } 29 | } 30 | 31 | func TestRobot(t *testing.T) { 32 | for distance, want := range map[int]int{ 33 | 1: 5 * 1, 34 | 2: 5 * 2, 35 | 3: 5 * 3, 36 | } { 37 | have := robot{}.run(distance) 38 | if want != have { 39 | t.Errorf("run(%d): want %d, have %d", distance, want, have) 40 | } 41 | } 42 | } 43 | 44 | func TestRace(t *testing.T) { 45 | for i, testcase := range []struct { 46 | distance int 47 | runners []runner 48 | want string 49 | }{ 50 | {40, []runner{baby{}}, "a baby"}, 51 | {50, []runner{baby{}, triathlete{}}, "a triathlete"}, 52 | {60, []runner{baby{}, triathlete{}}, "a baby"}, // lol 53 | } { 54 | if want, have := testcase.want, race(testcase.distance, testcase.runners...); want != have { 55 | t.Errorf("race %d: want %s, have %s", i+1, want, have) 56 | } 57 | } 58 | } 59 | 60 | func BenchmarkBaby1M(b *testing.B) { 61 | benchmark(b, baby{}, 1) 62 | } 63 | 64 | func BenchmarkBaby10M(b *testing.B) { 65 | benchmark(b, baby{}, 10) 66 | } 67 | 68 | func BenchmarkBaby100M(b *testing.B) { 69 | benchmark(b, baby{}, 100) 70 | } 71 | 72 | func BenchmarkTriathlete1M(b *testing.B) { 73 | benchmark(b, triathlete{}, 1) 74 | } 75 | 76 | func BenchmarkTriathlete10M(b *testing.B) { 77 | benchmark(b, triathlete{}, 10) 78 | } 79 | 80 | func BenchmarkTriathlete100M(b *testing.B) { 81 | benchmark(b, triathlete{}, 100) 82 | } 83 | 84 | func BenchmarkRobot1M(b *testing.B) { 85 | benchmark(b, robot{}, 1) 86 | } 87 | 88 | func BenchmarkRobot10M(b *testing.B) { 89 | benchmark(b, robot{}, 10) 90 | } 91 | 92 | func BenchmarkRobot100M(b *testing.B) { 93 | benchmark(b, robot{}, 100) 94 | } 95 | 96 | func benchmark(b *testing.B, r runner, distance int) { 97 | for i := 0; i < b.N; i++ { 98 | r.run(distance) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /slices/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "golang.org/x/tour/pic" 4 | 5 | func Pic(dx, dy int) [][]uint8 { 6 | ss := make([][]uint8, dy) 7 | for y := 0; y < dy; y++ { 8 | s := make([]uint8, dx) 9 | for x := 0; x < dx; x++ { 10 | s[x] = uint8(x) ^ uint8(y) 11 | } 12 | ss[y] = s 13 | } 14 | return ss 15 | } 16 | 17 | func main() { 18 | pic.Show(Pic) 19 | } 20 | -------------------------------------------------------------------------------- /statemachine/README.md: -------------------------------------------------------------------------------- 1 | # statemachine 2 | 3 | ![State machine diagram](sm.png) 4 | 5 | ```go 6 | // Implement stateMachine. 7 | type stateMachine struct{} 8 | 9 | func main() { 10 | sm := newStateMachine() 11 | sm.send(1) // "state A + 1 => state B" 12 | sm.send(0) // "state B + 0 => state C" 13 | println(sm.state()) // "state C" 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /statemachine/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type stateMachine struct { 6 | tr chan int 7 | st chan string 8 | } 9 | 10 | func newStateMachine() *stateMachine { 11 | sm := &stateMachine{ 12 | tr: make(chan int), 13 | st: make(chan string), 14 | } 15 | go sm.loop() 16 | return sm 17 | } 18 | 19 | func (sm *stateMachine) loop() { 20 | state := "A" 21 | for { 22 | select { 23 | case i := <-sm.tr: 24 | fmt.Printf("state %s + %d => ", state, i) 25 | switch { 26 | case state == "A" && i == 0: 27 | state = "A" 28 | case state == "A" && i == 1: 29 | state = "B" 30 | case state == "B" && i == 0: 31 | state = "C" 32 | case state == "B" && i == 1: 33 | state = "A" 34 | case state == "C" && i == 0: 35 | state = "B" 36 | case state == "C" && i == 1: 37 | state = "A" 38 | } 39 | fmt.Printf("state %s\n", state) 40 | case sm.st <- "state " + state: 41 | } 42 | } 43 | } 44 | 45 | func (sm *stateMachine) send(i int) { sm.tr <- i } 46 | func (sm *stateMachine) state() string { return <-sm.st } 47 | 48 | func main() { 49 | sm := newStateMachine() 50 | sm.send(1) // "state A + 1 => state B" 51 | sm.send(0) // "state B + 0 => state C" 52 | println(sm.state()) // "state C" 53 | } 54 | -------------------------------------------------------------------------------- /statemachine/sm.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | rankdir=LR; 3 | 4 | A -> A [label="0"]; 5 | A -> B [label="1"]; 6 | 7 | B -> C [label="0"]; 8 | B -> A [label="1"]; 9 | 10 | C -> B [label="0"]; 11 | C -> A [label="1"]; 12 | } 13 | -------------------------------------------------------------------------------- /statemachine/sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterbourgon/go-training/ae312cb6892b3edcda2b4312bdb3783a9cd283ea/statemachine/sm.png -------------------------------------------------------------------------------- /wordcount/README.md: -------------------------------------------------------------------------------- 1 | # wordcount 2 | 3 | Write a program called wordcount that reads words from stdin and writes each 4 | word and its count to stdout. Hint: os.Stdin, ioutil.ReadAll, map[string]int. 5 | 6 | ``` 7 | $ echo foo bar foo bar foo | wordcount
 8 | foo: 2
 9 | bar: 3 10 | ``` 11 | 12 | Bonus: do it without buffering the stdin to memory.
Hint: bufio.NewScanner, 13 | bufio.ScanWords. 14 | -------------------------------------------------------------------------------- /wordcount/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | counts := map[string]int{} 11 | 12 | s := bufio.NewScanner(os.Stdin) 13 | s.Split(bufio.ScanWords) 14 | for s.Scan() { 15 | counts[s.Text()]++ 16 | } 17 | 18 | for word, count := range counts { 19 | fmt.Printf("%q: %d\n", word, count) 20 | } 21 | } 22 | --------------------------------------------------------------------------------