Adding golang examples

This commit is contained in:
Michael Reber 2019-11-18 15:02:35 +01:00
parent 143c066c2b
commit 1b608328ba
223 changed files with 4799 additions and 0 deletions

3
go/README.md Normal file
View File

@ -0,0 +1,3 @@
# Golang
Go, also known as Golang, is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. Go is syntactically similar to C, but with memory safety, garbage collection, structural typing, and CSP-style concurrency.

42
go/arrays/arrays.go Normal file
View File

@ -0,0 +1,42 @@
// In Go, an _array_ is a numbered sequence of elements of a
// specific length.
package main
import "fmt"
func main() {
// Here we create an array `a` that will hold exactly
// 5 `int`s. The type of elements and length are both
// part of the array's type. By default an array is
// zero-valued, which for `int`s means `0`s.
var a [5]int
fmt.Println("emp:", a)
// We can set a value at an index using the
// `array[index] = value` syntax, and get a value with
// `array[index]`.
a[4] = 100
fmt.Println("set:", a)
fmt.Println("get:", a[4])
// The builtin `len` returns the length of an array.
fmt.Println("len:", len(a))
// Use this syntax to declare and initialize an array
// in one line.
b := [5]int{1, 2, 3, 4, 5}
fmt.Println("dcl:", b)
// Array types are one-dimensional, but you can
// compose types to build multi-dimensional data
// structures.
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}

2
go/arrays/arrays.hash Normal file
View File

@ -0,0 +1,2 @@
305975d13d24223181d13f042b290906d86c1a0e
W7NwfDq8Vdw

12
go/arrays/arrays.sh Normal file
View File

@ -0,0 +1,12 @@
# Note that arrays appear in the form `[v1 v2 v3 ...]`
# when printed with `fmt.Println`.
$ go run arrays.go
emp: [0 0 0 0 0]
set: [0 0 0 0 100]
get: 100
len: 5
dcl: [1 2 3 4 5]
2d: [[0 1 2] [1 2 3]]
# You'll see _slices_ much more often than arrays in
# typical Go. We'll look at slices next.

View File

@ -0,0 +1,52 @@
// The primary mechanism for managing state in Go is
// communication over channels. We saw this for example
// with [worker pools](worker-pools). There are a few other
// options for managing state though. Here we'll
// look at using the `sync/atomic` package for _atomic
// counters_ accessed by multiple goroutines.
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
// We'll use an unsigned integer to represent our
// (always-positive) counter.
var ops uint64
// A WaitGroup will help us wait for all goroutines
// to finish their work.
var wg sync.WaitGroup
// We'll start 50 goroutines that each increment the
// counter exactly 1000 times.
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
for c := 0; c < 1000; c++ {
// To atomically increment the counter we
// use `AddUint64`, giving it the memory
// address of our `ops` counter with the
// `&` syntax.
atomic.AddUint64(&ops, 1)
}
wg.Done()
}()
}
// Wait until all the goroutines are done.
wg.Wait()
// It's safe to access `ops` now because we know
// no other goroutine is writing to it. Reading
// atomics safely while they are being updated is
// also possible, using functions like
// `atomic.LoadUint64`.
fmt.Println("ops:", ops)
}

View File

@ -0,0 +1,2 @@
8ebec0be3b167021c96b8b497d0e8c0a2ea99385
F2pJfduyQiA

View File

@ -0,0 +1,11 @@
# We expect to get exactly 50,000 operations. Had we
# used the non-atomic `ops++` to increment the counter,
# we'd likely get a different number, changing between
# runs, because the goroutines would interfere with
# each other. Moreover, we'd get data race failures
# when running with the `-race` flag.
$ go run atomic-counters.go
ops: 50000
# Next we'll look at mutexes, another tool for managing
# state.

View File

@ -0,0 +1,39 @@
// Go provides built-in support for [base64
// encoding/decoding](http://en.wikipedia.org/wiki/Base64).
package main
// This syntax imports the `encoding/base64` package with
// the `b64` name instead of the default `base64`. It'll
// save us some space below.
import (
b64 "encoding/base64"
"fmt"
)
func main() {
// Here's the `string` we'll encode/decode.
data := "abc123!?$*&()'-=@~"
// Go supports both standard and URL-compatible
// base64. Here's how to encode using the standard
// encoder. The encoder requires a `[]byte` so we
// convert our `string` to that type.
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
fmt.Println(sEnc)
// Decoding may return an error, which you can check
// if you don't already know the input to be
// well-formed.
sDec, _ := b64.StdEncoding.DecodeString(sEnc)
fmt.Println(string(sDec))
fmt.Println()
// This encodes/decodes using a URL-compatible base64
// format.
uEnc := b64.URLEncoding.EncodeToString([]byte(data))
fmt.Println(uEnc)
uDec, _ := b64.URLEncoding.DecodeString(uEnc)
fmt.Println(string(uDec))
}

View File

@ -0,0 +1,2 @@
e0148b9b4acb01e849b8f678cba03f549d250c44
V3oV1bvh94k

View File

@ -0,0 +1,9 @@
# The string encodes to slightly different values with the
# standard and URL base64 encoders (trailing `+` vs `-`)
# but they both decode to the original string as desired.
$ go run base64-encoding.go
YWJjMTIzIT8kKiYoKSctPUB+
abc123!?$*&()'-=@~
YWJjMTIzIT8kKiYoKSctPUB-
abc123!?$*&()'-=@~

View File

@ -0,0 +1,27 @@
// By default channels are _unbuffered_, meaning that they
// will only accept sends (`chan <-`) if there is a
// corresponding receive (`<- chan`) ready to receive the
// sent value. _Buffered channels_ accept a limited
// number of values without a corresponding receiver for
// those values.
package main
import "fmt"
func main() {
// Here we `make` a channel of strings buffering up to
// 2 values.
messages := make(chan string, 2)
// Because this channel is buffered, we can send these
// values into the channel without a corresponding
// concurrent receive.
messages <- "buffered"
messages <- "channel"
// Later we can receive these two values as usual.
fmt.Println(<-messages)
fmt.Println(<-messages)
}

View File

@ -0,0 +1,2 @@
122140f7ad1bc5cff4fcd7a9e7245b87aaca3ec5
mPoF-Xi-rip

View File

@ -0,0 +1,3 @@
$ go run channel-buffering.go
buffered
channel

View File

@ -0,0 +1,30 @@
// When using channels as function parameters, you can
// specify if a channel is meant to only send or receive
// values. This specificity increases the type-safety of
// the program.
package main
import "fmt"
// This `ping` function only accepts a channel for sending
// values. It would be a compile-time error to try to
// receive on this channel.
func ping(pings chan<- string, msg string) {
pings <- msg
}
// The `pong` function accepts one channel for receives
// (`pings`) and a second for sends (`pongs`).
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
}
func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "passed message")
pong(pings, pongs)
fmt.Println(<-pongs)
}

View File

@ -0,0 +1,2 @@
635cc13dfe33123ac188e01e3002d3aa935d765f
Jnn9_9hC48c

View File

@ -0,0 +1,2 @@
$ go run channel-directions.go
passed message

View File

@ -0,0 +1,36 @@
// We can use channels to synchronize execution
// across goroutines. Here's an example of using a
// blocking receive to wait for a goroutine to finish.
// When waiting for multiple goroutines to finish,
// you may prefer to use a [WaitGroup](waitgroups).
package main
import (
"fmt"
"time"
)
// This is the function we'll run in a goroutine. The
// `done` channel will be used to notify another
// goroutine that this function's work is done.
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
// Send a value to notify that we're done.
done <- true
}
func main() {
// Start a worker goroutine, giving it the channel to
// notify on.
done := make(chan bool, 1)
go worker(done)
// Block until we receive a notification from the
// worker on the channel.
<-done
}

View File

@ -0,0 +1,2 @@
0d5a9de912e2a4a18943e082e2f8107cb75d0ce4
fe9If6OhYMk

View File

@ -0,0 +1,6 @@
$ go run channel-synchronization.go
working...done
# If you removed the `<- done` line from this program, the
# program would exit before the `worker` even
# started.

26
go/channels/channels.go Normal file
View File

@ -0,0 +1,26 @@
// _Channels_ are the pipes that connect concurrent
// goroutines. You can send values into channels from one
// goroutine and receive those values into another
// goroutine.
package main
import "fmt"
func main() {
// Create a new channel with `make(chan val-type)`.
// Channels are typed by the values they convey.
messages := make(chan string)
// _Send_ a value into a channel using the `channel <-`
// syntax. Here we send `"ping"` to the `messages`
// channel we made above, from a new goroutine.
go func() { messages <- "ping" }()
// The `<-channel` syntax _receives_ a value from the
// channel. Here we'll receive the `"ping"` message
// we sent above and print it out.
msg := <-messages
fmt.Println(msg)
}

View File

@ -0,0 +1,2 @@
926212c784ab820648906c96f6ab21afbc161526
bRGMAqinovA

10
go/channels/channels.sh Normal file
View File

@ -0,0 +1,10 @@
# When we run the program the `"ping"` message is
# successfully passed from one goroutine to another via
# our channel.
$ go run channels.go
ping
# By default sends and receives block until both the
# sender and receiver are ready. This property allowed
# us to wait at the end of our program for the `"ping"`
# message without having to use any other synchronization.

View File

@ -0,0 +1,50 @@
// _Closing_ a channel indicates that no more values
// will be sent on it. This can be useful to communicate
// completion to the channel's receivers.
package main
import "fmt"
// In this example we'll use a `jobs` channel to
// communicate work to be done from the `main()` goroutine
// to a worker goroutine. When we have no more jobs for
// the worker we'll `close` the `jobs` channel.
func main() {
jobs := make(chan int, 5)
done := make(chan bool)
// Here's the worker goroutine. It repeatedly receives
// from `jobs` with `j, more := <-jobs`. In this
// special 2-value form of receive, the `more` value
// will be `false` if `jobs` has been `close`d and all
// values in the channel have already been received.
// We use this to notify on `done` when we've worked
// all our jobs.
go func() {
for {
j, more := <-jobs
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
done <- true
return
}
}
}()
// This sends 3 jobs to the worker over the `jobs`
// channel, then closes it.
for j := 1; j <= 3; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs)
fmt.Println("sent all jobs")
// We await the worker using the
// [synchronization](channel-synchronization) approach
// we saw earlier.
<-done
}

View File

@ -0,0 +1,2 @@
5205898a520533e46ea24c849848d19ebc2d08a9
mkz69rVMHs6

View File

@ -0,0 +1,12 @@
$ go run closing-channels.go
sent job 1
received job 1
sent job 2
received job 2
sent job 3
received job 3
sent all jobs
received all jobs
# The idea of closed channels leads naturally to our next
# example: `range` over channels.

40
go/closures/closures.go Normal file
View File

@ -0,0 +1,40 @@
// Go supports [_anonymous functions_](http://en.wikipedia.org/wiki/Anonymous_function),
// which can form <a href="http://en.wikipedia.org/wiki/Closure_(computer_science)"><em>closures</em></a>.
// Anonymous functions are useful when you want to define
// a function inline without having to name it.
package main
import "fmt"
// This function `intSeq` returns another function, which
// we define anonymously in the body of `intSeq`. The
// returned function _closes over_ the variable `i` to
// form a closure.
func intSeq() func() int {
i := 0
return func() int {
i++
return i
}
}
func main() {
// We call `intSeq`, assigning the result (a function)
// to `nextInt`. This function value captures its
// own `i` value, which will be updated each time
// we call `nextInt`.
nextInt := intSeq()
// See the effect of the closure by calling `nextInt`
// a few times.
fmt.Println(nextInt())
fmt.Println(nextInt())
fmt.Println(nextInt())
// To confirm that the state is unique to that
// particular function, create and test a new one.
newInts := intSeq()
fmt.Println(newInts())
}

View File

@ -0,0 +1,2 @@
e304df67e760dda93ffe434aca58aea4a6c94f19
zb93qzV6iN3

8
go/closures/closures.sh Normal file
View File

@ -0,0 +1,8 @@
$ go run closures.go
1
2
3
1
# The last feature of functions we'll look at for now is
# recursion.

View File

@ -0,0 +1,113 @@
// We often need our programs to perform operations on
// collections of data, like selecting all items that
// satisfy a given predicate or mapping all items to a new
// collection with a custom function.
// In some languages it's idiomatic to use [generic](http://en.wikipedia.org/wiki/Generic_programming)
// data structures and algorithms. Go does not support
// generics; in Go it's common to provide collection
// functions if and when they are specifically needed for
// your program and data types.
// Here are some example collection functions for slices
// of `strings`. You can use these examples to build your
// own functions. Note that in some cases it may be
// clearest to just inline the collection-manipulating
// code directly, instead of creating and calling a
// helper function.
package main
import (
"fmt"
"strings"
)
// Index returns the first index of the target string `t`, or
// -1 if no match is found.
func Index(vs []string, t string) int {
for i, v := range vs {
if v == t {
return i
}
}
return -1
}
// Include returns `true` if the target string t is in the
// slice.
func Include(vs []string, t string) bool {
return Index(vs, t) >= 0
}
// Any returns `true` if one of the strings in the slice
// satisfies the predicate `f`.
func Any(vs []string, f func(string) bool) bool {
for _, v := range vs {
if f(v) {
return true
}
}
return false
}
// All returns `true` if all of the strings in the slice
// satisfy the predicate `f`.
func All(vs []string, f func(string) bool) bool {
for _, v := range vs {
if !f(v) {
return false
}
}
return true
}
// Filter returns a new slice containing all strings in the
// slice that satisfy the predicate `f`.
func Filter(vs []string, f func(string) bool) []string {
vsf := make([]string, 0)
for _, v := range vs {
if f(v) {
vsf = append(vsf, v)
}
}
return vsf
}
// Map returns a new slice containing the results of applying
// the function `f` to each string in the original slice.
func Map(vs []string, f func(string) string) []string {
vsm := make([]string, len(vs))
for i, v := range vs {
vsm[i] = f(v)
}
return vsm
}
func main() {
// Here we try out our various collection functions.
var strs = []string{"peach", "apple", "pear", "plum"}
fmt.Println(Index(strs, "pear"))
fmt.Println(Include(strs, "grape"))
fmt.Println(Any(strs, func(v string) bool {
return strings.HasPrefix(v, "p")
}))
fmt.Println(All(strs, func(v string) bool {
return strings.HasPrefix(v, "p")
}))
fmt.Println(Filter(strs, func(v string) bool {
return strings.Contains(v, "e")
}))
// The above examples all used anonymous functions,
// but you can also use named functions of the correct
// type.
fmt.Println(Map(strs, strings.ToUpper))
}

View File

@ -0,0 +1,2 @@
28456737ea996664bdaeb8e1e821d95697d00646
8hI6oPNEfyh

View File

@ -0,0 +1,7 @@
$ go run collection-functions.go
2
false
true
false
[peach apple pear]
[PEACH APPLE PEAR PLUM]

View File

@ -0,0 +1,28 @@
// [_Command-line arguments_](http://en.wikipedia.org/wiki/Command-line_interface#Arguments)
// are a common way to parameterize execution of programs.
// For example, `go run hello.go` uses `run` and
// `hello.go` arguments to the `go` program.
package main
import (
"fmt"
"os"
)
func main() {
// `os.Args` provides access to raw command-line
// arguments. Note that the first value in this slice
// is the path to the program, and `os.Args[1:]`
// holds the arguments to the program.
argsWithProg := os.Args
argsWithoutProg := os.Args[1:]
// You can get individual args with normal indexing.
arg := os.Args[3]
fmt.Println(argsWithProg)
fmt.Println(argsWithoutProg)
fmt.Println(arg)
}

View File

@ -0,0 +1,2 @@
9c80d495201148bbeb0fd0a5a2ce1735aeb34b60
myJy_-H8Fo_Q

View File

@ -0,0 +1,10 @@
# To experiment with command-line arguments it's best to
# build a binary with `go build` first.
$ go build command-line-arguments.go
$ ./command-line-arguments a b c d
[./command-line-arguments a b c d]
[a b c d]
c
# Next we'll look at more advanced command-line processing
# with flags.

View File

@ -0,0 +1,51 @@
// [_Command-line flags_](http://en.wikipedia.org/wiki/Command-line_interface#Command-line_option)
// are a common way to specify options for command-line
// programs. For example, in `wc -l` the `-l` is a
// command-line flag.
package main
// Go provides a `flag` package supporting basic
// command-line flag parsing. We'll use this package to
// implement our example command-line program.
import (
"flag"
"fmt"
)
func main() {
// Basic flag declarations are available for string,
// integer, and boolean options. Here we declare a
// string flag `word` with a default value `"foo"`
// and a short description. This `flag.String` function
// returns a string pointer (not a string value);
// we'll see how to use this pointer below.
wordPtr := flag.String("word", "foo", "a string")
// This declares `numb` and `fork` flags, using a
// similar approach to the `word` flag.
numbPtr := flag.Int("numb", 42, "an int")
boolPtr := flag.Bool("fork", false, "a bool")
// It's also possible to declare an option that uses an
// existing var declared elsewhere in the program.
// Note that we need to pass in a pointer to the flag
// declaration function.
var svar string
flag.StringVar(&svar, "svar", "bar", "a string var")
// Once all flags are declared, call `flag.Parse()`
// to execute the command-line parsing.
flag.Parse()
// Here we'll just dump out the parsed options and
// any trailing positional arguments. Note that we
// need to dereference the pointers with e.g. `*wordPtr`
// to get the actual option values.
fmt.Println("word:", *wordPtr)
fmt.Println("numb:", *numbPtr)
fmt.Println("fork:", *boolPtr)
fmt.Println("svar:", svar)
fmt.Println("tail:", flag.Args())
}

View File

@ -0,0 +1,2 @@
ab08bf890dcd87b807956b5bee441d59efe458e3
lPaZodnG9TF

View File

@ -0,0 +1,56 @@
# To experiment with the command-line flags program it's
# best to first compile it and then run the resulting
# binary directly.
$ go build command-line-flags.go
# Try out the built program by first giving it values for
# all flags.
$ ./command-line-flags -word=opt -numb=7 -fork -svar=flag
word: opt
numb: 7
fork: true
svar: flag
tail: []
# Note that if you omit flags they automatically take
# their default values.
$ ./command-line-flags -word=opt
word: opt
numb: 42
fork: false
svar: bar
tail: []
# Trailing positional arguments can be provided after
# any flags.
$ ./command-line-flags -word=opt a1 a2 a3
word: opt
...
tail: [a1 a2 a3]
# Note that the `flag` package requires all flags to
# appear before positional arguments (otherwise the flags
# will be interpreted as positional arguments).
$ ./command-line-flags -word=opt a1 a2 a3 -numb=7
word: opt
numb: 42
fork: false
svar: bar
tail: [a1 a2 a3 -numb=7]
# Use `-h` or `--help` flags to get automatically
# generated help text for the command-line program.
$ ./command-line-flags -h
Usage of ./command-line-flags:
-fork=false: a bool
-numb=42: an int
-svar="bar": a string var
-word="foo": a string
# If you provide a flag that wasn't specified to the
# `flag` package, the program will print an error message
# and show the help text again.
$ ./command-line-flags -wat
flag provided but not defined: -wat
Usage of ./command-line-flags:
...

View File

@ -0,0 +1,57 @@
// Some command-line tools, like the `go` tool or `git`
// have many *subcommands*, each with its own set of
// flags. For example, `go build` and `go get` are two
// different subcommands of the `go` tool.
// The `flag` package lets us easily define simple
// subcommands that have their own flags.
package main
import (
"flag"
"fmt"
"os"
)
func main() {
// We declare a subcommand using the `NewFlagSet`
// function, and proceed to define new flags specific
// for this subcommand.
fooCmd := flag.NewFlagSet("foo", flag.ExitOnError)
fooEnable := fooCmd.Bool("enable", false, "enable")
fooName := fooCmd.String("name", "", "name")
// For a different subcommand we can define different
// supported flags.
barCmd := flag.NewFlagSet("bar", flag.ExitOnError)
barLevel := barCmd.Int("level", 0, "level")
// The subcommand is expected as the first argument
// to the program.
if len(os.Args) < 2 {
fmt.Println("expected 'foo' or 'bar' subcommands")
os.Exit(1)
}
// Check which subcommand is invoked.
switch os.Args[1] {
// For every subcommand, we parse its own flags and
// have access to trailing positional arguments.
case "foo":
fooCmd.Parse(os.Args[2:])
fmt.Println("subcommand 'foo'")
fmt.Println(" enable:", *fooEnable)
fmt.Println(" name:", *fooName)
fmt.Println(" tail:", fooCmd.Args())
case "bar":
barCmd.Parse(os.Args[2:])
fmt.Println("subcommand 'bar'")
fmt.Println(" level:", *barLevel)
fmt.Println(" tail:", barCmd.Args())
default:
fmt.Println("expected 'foo' or 'bar' subcommands")
os.Exit(1)
}
}

View File

@ -0,0 +1,2 @@
5a0ec258e4992e9b93b11d48f2f249092ff3db66
gtgSAg76N4I

View File

@ -0,0 +1,24 @@
$ go build command-line-subcommands.go
# First invoke the foo subcommand.
$ ./command-line-subcommands foo -enable -name=joe a1 a2
subcommand 'foo'
enable: true
name: joe
tail: [a1 a2]
# Now try bar.
$ ./command-line-subcommands bar -level 8 a1
subcommand 'bar'
level: 8
tail: [a1]
# But bar won't accept foo's flags.
$ ./command-line-subcommands bar -enable a1
flag provided but not defined: -enable
Usage of bar:
-level int
level
# Next we'll look at environment variables, another common
# way to parameterize programs.

35
go/constants/constants.go Normal file
View File

@ -0,0 +1,35 @@
// Go supports _constants_ of character, string, boolean,
// and numeric values.
package main
import (
"fmt"
"math"
)
// `const` declares a constant value.
const s string = "constant"
func main() {
fmt.Println(s)
// A `const` statement can appear anywhere a `var`
// statement can.
const n = 500000000
// Constant expressions perform arithmetic with
// arbitrary precision.
const d = 3e20 / n
fmt.Println(d)
// A numeric constant has no type until it's given
// one, such as by an explicit conversion.
fmt.Println(int64(d))
// A number can be given a type by using it in a
// context that requires one, such as a variable
// assignment or function call. For example, here
// `math.Sin` expects a `float64`.
fmt.Println(math.Sin(n))
}

View File

@ -0,0 +1,2 @@
7cc09460e8dc6fffd0ba811679f92a85eb51e927
gmjHSglwLic

View File

@ -0,0 +1,5 @@
$ go run constant.go
constant
6e+11
600000000000
-0.28470407323754404

52
go/defer/defer.go Normal file
View File

@ -0,0 +1,52 @@
// _Defer_ is used to ensure that a function call is
// performed later in a program's execution, usually for
// purposes of cleanup. `defer` is often used where e.g.
// `ensure` and `finally` would be used in other languages.
package main
import (
"fmt"
"os"
)
// Suppose we wanted to create a file, write to it,
// and then close when we're done. Here's how we could
// do that with `defer`.
func main() {
// Immediately after getting a file object with
// `createFile`, we defer the closing of that file
// with `closeFile`. This will be executed at the end
// of the enclosing function (`main`), after
// `writeFile` has finished.
f := createFile("/tmp/defer.txt")
defer closeFile(f)
writeFile(f)
}
func createFile(p string) *os.File {
fmt.Println("creating")
f, err := os.Create(p)
if err != nil {
panic(err)
}
return f
}
func writeFile(f *os.File) {
fmt.Println("writing")
fmt.Fprintln(f, "data")
}
func closeFile(f *os.File) {
fmt.Println("closing")
err := f.Close()
// It's important to check for errors when closing a
// file, even in a deferred function.
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}

2
go/defer/defer.hash Normal file
View File

@ -0,0 +1,2 @@
4fc23579a9bd5d8512884902394f4dce51eb7d60
QJJ2R6kv6K5

6
go/defer/defer.sh Normal file
View File

@ -0,0 +1,6 @@
# Running the program confirms that the file is closed
# after being written.
$ go run defer.go
creating
writing
closing

View File

@ -0,0 +1,95 @@
// Go has several useful functions for working with
// *directories* in the file system.
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// Create a new sub-directory in the current working
// directory.
err := os.Mkdir("subdir", 0755)
check(err)
// When creating temporary directories, it's good
// practice to `defer` their removal. `os.RemoveAll`
// will delete a whole directory tree (similarly to
// `rm -rf`).
defer os.RemoveAll("subdir")
// Helper function to create a new empty file.
createEmptyFile := func(name string) {
d := []byte("")
check(ioutil.WriteFile(name, d, 0644))
}
createEmptyFile("subdir/file1")
// We can create a hierarchy of directories, including
// parents with `MkdirAll`. This is similar to the
// command-line `mkdir -p`.
err = os.MkdirAll("subdir/parent/child", 0755)
check(err)
createEmptyFile("subdir/parent/file2")
createEmptyFile("subdir/parent/file3")
createEmptyFile("subdir/parent/child/file4")
// `ReadDir` lists directory contents, returning a
// slice of `os.FileInfo` objects.
c, err := ioutil.ReadDir("subdir/parent")
check(err)
fmt.Println("Listing subdir/parent")
for _, entry := range c {
fmt.Println(" ", entry.Name(), entry.IsDir())
}
// `Chdir` lets us change the current working directory,
// similarly to `cd`.
err = os.Chdir("subdir/parent/child")
check(err)
// Now we'll see the contents of `subdir/parent/child`
// when listing the *current* directory.
c, err = ioutil.ReadDir(".")
check(err)
fmt.Println("Listing subdir/parent/child")
for _, entry := range c {
fmt.Println(" ", entry.Name(), entry.IsDir())
}
// `cd` back to where we started.
err = os.Chdir("../../..")
check(err)
// We can also visit a directory *recursively*,
// including all its sub-directories. `Walk` accepts
// a callback function to handle every file or
// directory visited.
fmt.Println("Visiting subdir")
err = filepath.Walk("subdir", visit)
}
// `visit` is called for every file or directory found
// recursively by `filepath.Walk`.
func visit(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fmt.Println(" ", p, info.IsDir())
return nil
}

View File

@ -0,0 +1,2 @@
83f67db91816b4544072d0a4d099111a21c60723
-7kWq0PmATF

View File

@ -0,0 +1,15 @@
$ go run directories.go
Listing subdir/parent
child true
file2 false
file3 false
Listing subdir/parent/child
file4 false
Visiting subdir
subdir true
subdir/file1 false
subdir/parent true
subdir/parent/child true
subdir/parent/child/file4 false
subdir/parent/file2 false
subdir/parent/file3 false

View File

@ -0,0 +1,33 @@
// [Environment variables](http://en.wikipedia.org/wiki/Environment_variable)
// are a universal mechanism for [conveying configuration
// information to Unix programs](http://www.12factor.net/config).
// Let's look at how to set, get, and list environment variables.
package main
import (
"fmt"
"os"
"strings"
)
func main() {
// To set a key/value pair, use `os.Setenv`. To get a
// value for a key, use `os.Getenv`. This will return
// an empty string if the key isn't present in the
// environment.
os.Setenv("FOO", "1")
fmt.Println("FOO:", os.Getenv("FOO"))
fmt.Println("BAR:", os.Getenv("BAR"))
// Use `os.Environ` to list all key/value pairs in the
// environment. This returns a slice of strings in the
// form `KEY=value`. You can `strings.SplitN` them to
// get the key and value. Here we print all the keys.
fmt.Println()
for _, e := range os.Environ() {
pair := strings.SplitN(e, "=", 2)
fmt.Println(pair[0])
}
}

View File

@ -0,0 +1,2 @@
69d6a768ac84c873ae03b2169ac5cc4cfbc601a6
gSTxKWLOHRb

View File

@ -0,0 +1,20 @@
# Running the program shows that we pick up the value
# for `FOO` that we set in the program, but that
# `BAR` is empty.
$ go run environment-variables.go
FOO: 1
BAR:
# The list of keys in the environment will depend on your
# particular machine.
TERM_PROGRAM
PATH
SHELL
...
# If we set `BAR` in the environment first, the running
# program picks that value up.
$ BAR=2 go run environment-variables.go
FOO: 1
BAR: 2
...

35
go/epoch/epoch.go Normal file
View File

@ -0,0 +1,35 @@
// A common requirement in programs is getting the number
// of seconds, milliseconds, or nanoseconds since the
// [Unix epoch](http://en.wikipedia.org/wiki/Unix_time).
// Here's how to do it in Go.
package main
import (
"fmt"
"time"
)
func main() {
// Use `time.Now` with `Unix` or `UnixNano` to get
// elapsed time since the Unix epoch in seconds or
// nanoseconds, respectively.
now := time.Now()
secs := now.Unix()
nanos := now.UnixNano()
fmt.Println(now)
// Note that there is no `UnixMillis`, so to get the
// milliseconds since epoch you'll need to manually
// divide from nanoseconds.
millis := nanos / 1000000
fmt.Println(secs)
fmt.Println(millis)
fmt.Println(nanos)
// You can also convert integer seconds or nanoseconds
// since the epoch into the corresponding `time`.
fmt.Println(time.Unix(secs, 0))
fmt.Println(time.Unix(0, nanos))
}

2
go/epoch/epoch.hash Normal file
View File

@ -0,0 +1,2 @@
184c8f3e94dd28176be81956115ac417e33513a6
3uYNHHRplmQ

10
go/epoch/epoch.sh Normal file
View File

@ -0,0 +1,10 @@
$ go run epoch.go
2012-10-31 16:13:58.292387 +0000 UTC
1351700038
1351700038292
1351700038292387000
2012-10-31 16:13:58 +0000 UTC
2012-10-31 16:13:58.292387 +0000 UTC
# Next we'll look at another time-related task: time
# parsing and formatting.

87
go/errors/errors.go Normal file
View File

@ -0,0 +1,87 @@
// In Go it's idiomatic to communicate errors via an
// explicit, separate return value. This contrasts with
// the exceptions used in languages like Java and Ruby and
// the overloaded single result / error value sometimes
// used in C. Go's approach makes it easy to see which
// functions return errors and to handle them using the
// same language constructs employed for any other,
// non-error tasks.
package main
import (
"errors"
"fmt"
)
// By convention, errors are the last return value and
// have type `error`, a built-in interface.
func f1(arg int) (int, error) {
if arg == 42 {
// `errors.New` constructs a basic `error` value
// with the given error message.
return -1, errors.New("can't work with 42")
}
// A `nil` value in the error position indicates that
// there was no error.
return arg + 3, nil
}
// It's possible to use custom types as `error`s by
// implementing the `Error()` method on them. Here's a
// variant on the example above that uses a custom type
// to explicitly represent an argument error.
type argError struct {
arg int
prob string
}
func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f2(arg int) (int, error) {
if arg == 42 {
// In this case we use `&argError` syntax to build
// a new struct, supplying values for the two
// fields `arg` and `prob`.
return -1, &argError{arg, "can't work with it"}
}
return arg + 3, nil
}
func main() {
// The two loops below test out each of our
// error-returning functions. Note that the use of an
// inline error check on the `if` line is a common
// idiom in Go code.
for _, i := range []int{7, 42} {
if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e)
} else {
fmt.Println("f1 worked:", r)
}
}
for _, i := range []int{7, 42} {
if r, e := f2(i); e != nil {
fmt.Println("f2 failed:", e)
} else {
fmt.Println("f2 worked:", r)
}
}
// If you want to programmatically use the data in
// a custom error, you'll need to get the error as an
// instance of the custom error type via type
// assertion.
_, e := f2(42)
if ae, ok := e.(*argError); ok {
fmt.Println(ae.arg)
fmt.Println(ae.prob)
}
}

2
go/errors/errors.hash Normal file
View File

@ -0,0 +1,2 @@
ed58ad3162d93c723d3efe72529a61ce7041fe13
vrwN32gxaYx

10
go/errors/errors.sh Normal file
View File

@ -0,0 +1,10 @@
$ go run errors.go
f1 worked: 10
f1 failed: can't work with 42
f2 worked: 10
f2 failed: 42 - can't work with it
42
can't work with it
# See this [great post](http://blog.golang.org/2011/07/error-handling-and-go.html)
# on the Go blog for more on error handling.

View File

@ -0,0 +1,50 @@
// In the previous example we looked at
// [spawning external processes](spawning-processes). We
// do this when we need an external process accessible to
// a running Go process. Sometimes we just want to
// completely replace the current Go process with another
// (perhaps non-Go) one. To do this we'll use Go's
// implementation of the classic
// <a href="http://en.wikipedia.org/wiki/Exec_(operating_system)"><code>exec</code></a>
// function.
package main
import (
"os"
"os/exec"
"syscall"
)
func main() {
// For our example we'll exec `ls`. Go requires an
// absolute path to the binary we want to execute, so
// we'll use `exec.LookPath` to find it (probably
// `/bin/ls`).
binary, lookErr := exec.LookPath("ls")
if lookErr != nil {
panic(lookErr)
}
// `Exec` requires arguments in slice form (as
// apposed to one big string). We'll give `ls` a few
// common arguments. Note that the first argument should
// be the program name.
args := []string{"ls", "-a", "-l", "-h"}
// `Exec` also needs a set of [environment variables](environment-variables)
// to use. Here we just provide our current
// environment.
env := os.Environ()
// Here's the actual `syscall.Exec` call. If this call is
// successful, the execution of our process will end
// here and be replaced by the `/bin/ls -a -l -h`
// process. If there is an error we'll get a return
// value.
execErr := syscall.Exec(binary, args, env)
if execErr != nil {
panic(execErr)
}
}

View File

@ -0,0 +1,2 @@
e6b4830d4264f307506b54726ec79b25a0363874
ahZjpJaZz44

View File

@ -0,0 +1,11 @@
# When we run our program it is replaced by `ls`.
$ go run execing-processes.go
total 16
drwxr-xr-x 4 mark 136B Oct 3 16:29 .
drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
-rw-r--r-- 1 mark 1.3K Oct 3 16:28 execing-processes.go
# Note that Go does not offer a classic Unix `fork`
# function. Usually this isn't an issue though, since
# starting goroutines, spawning processes, and exec'ing
# processes covers most use cases for `fork`.

24
go/exit/exit.go Normal file
View File

@ -0,0 +1,24 @@
// Use `os.Exit` to immediately exit with a given
// status.
package main
import (
"fmt"
"os"
)
func main() {
// `defer`s will _not_ be run when using `os.Exit`, so
// this `fmt.Println` will never be called.
defer fmt.Println("!")
// Exit with status 3.
os.Exit(3)
}
// Note that unlike e.g. C, Go does not use an integer
// return value from `main` to indicate exit status. If
// you'd like to exit with a non-zero status you should
// use `os.Exit`.

2
go/exit/exit.hash Normal file
View File

@ -0,0 +1,2 @@
2316e6c8e364e2066c6bd350dc9b0b2af85b63fe
OX997ykuOGx

13
go/exit/exit.sh Normal file
View File

@ -0,0 +1,13 @@
# If you run `exit.go` using `go run`, the exit
# will be picked up by `go` and printed.
$ go run exit.go
exit status 3
# By building and executing a binary you can see
# the status in the terminal.
$ go build exit.go
$ ./exit
$ echo $?
3
# Note that the `!` from our program never got printed.

View File

@ -0,0 +1,64 @@
// The `filepath` package provides functions to parse
// and construct *file paths* in a way that is portable
// between operating systems; `dir/file` on Linux vs.
// `dir\file` on Windows, for example.
package main
import (
"fmt"
"path/filepath"
"strings"
)
func main() {
// `Join` should be used to construct paths in a
// portable way. It takes any number of arguments
// and constructs a hierarchical path from them.
p := filepath.Join("dir1", "dir2", "filename")
fmt.Println("p:", p)
// You should always use `Join` instead of
// concatenating `/`s or `\`s manually. In addition
// to providing portability, `Join` will also
// normalize paths by removing superfluous separators
// and directory changes.
fmt.Println(filepath.Join("dir1//", "filename"))
fmt.Println(filepath.Join("dir1/../dir1", "filename"))
// `Dir` and `Base` can be used to split a path to the
// directory and the file. Alternatively, `Split` will
// return both in the same call.
fmt.Println("Dir(p):", filepath.Dir(p))
fmt.Println("Base(p):", filepath.Base(p))
// We can check whether a path is absolute.
fmt.Println(filepath.IsAbs("dir/file"))
fmt.Println(filepath.IsAbs("/dir/file"))
filename := "config.json"
// Some file names have extensions following a dot. We
// can split the extension out of such names with `Ext`.
ext := filepath.Ext(filename)
fmt.Println(ext)
// To find the file's name with the extension removed,
// use `strings.TrimSuffix`.
fmt.Println(strings.TrimSuffix(filename, ext))
// `Rel` finds a relative path between a *base* and a
// *target*. It returns an error if the target cannot
// be made relative to base.
rel, err := filepath.Rel("a/b", "a/b/t/file")
if err != nil {
panic(err)
}
fmt.Println(rel)
rel, err = filepath.Rel("a/b", "a/c/t/file")
if err != nil {
panic(err)
}
fmt.Println(rel)
}

View File

@ -0,0 +1,2 @@
1215302b9e59ee9dee21dcd3c47d5f6c672fb058
QIitbMNiFRx

View File

@ -0,0 +1,12 @@
$ go run file-paths.go
p: dir1/dir2/filename
dir1/filename
dir1/filename
Dir(p): dir1/dir2
Base(p): filename
false
true
.json
config
t/file
../c/t/file

38
go/for/for.go Normal file
View File

@ -0,0 +1,38 @@
// `for` is Go's only looping construct. Here are
// three basic types of `for` loops.
package main
import "fmt"
func main() {
// The most basic type, with a single condition.
i := 1
for i <= 3 {
fmt.Println(i)
i = i + 1
}
// A classic initial/condition/after `for` loop.
for j := 7; j <= 9; j++ {
fmt.Println(j)
}
// `for` without a condition will loop repeatedly
// until you `break` out of the loop or `return` from
// the enclosing function.
for {
fmt.Println("loop")
break
}
// You can also `continue` to the next iteration of
// the loop.
for n := 0; n <= 5; n++ {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
}

2
go/for/for.hash Normal file
View File

@ -0,0 +1,2 @@
33056d6b36f9894fb6359c9cf2ef8725bbdafa19
lGYfUJwiGfi

15
go/for/for.sh Normal file
View File

@ -0,0 +1,15 @@
$ go run for.go
1
2
3
7
8
9
loop
1
3
5
# We'll see some other `for` forms later when we look at
# `range` statements, channels, and other data
# structures.

35
go/functions/functions.go Normal file
View File

@ -0,0 +1,35 @@
// _Functions_ are central in Go. We'll learn about
// functions with a few different examples.
package main
import "fmt"
// Here's a function that takes two `int`s and returns
// their sum as an `int`.
func plus(a int, b int) int {
// Go requires explicit returns, i.e. it won't
// automatically return the value of the last
// expression.
return a + b
}
// When you have multiple consecutive parameters of
// the same type, you may omit the type name for the
// like-typed parameters up to the final parameter that
// declares the type.
func plusPlus(a, b, c int) int {
return a + b + c
}
func main() {
// Call a function just as you'd expect, with
// `name(args)`.
res := plus(1, 2)
fmt.Println("1+2 =", res)
res = plusPlus(1, 2, 3)
fmt.Println("1+2+3 =", res)
}

View File

@ -0,0 +1,2 @@
ae669923c20e5ebf4a7b4b11b8fdf2972accf9e2
hzGUvK6iJNm

View File

@ -0,0 +1,6 @@
$ go run functions.go
1+2 = 3
1+2+3 = 6
# There are several other features to Go functions. One is
# multiple return values, which we'll look at next.

View File

@ -0,0 +1,39 @@
// A _goroutine_ is a lightweight thread of execution.
package main
import (
"fmt"
"time"
)
func f(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
}
}
func main() {
// Suppose we have a function call `f(s)`. Here's how
// we'd call that in the usual way, running it
// synchronously.
f("direct")
// To invoke this function in a goroutine, use
// `go f(s)`. This new goroutine will execute
// concurrently with the calling one.
go f("goroutine")
// You can also start a goroutine for an anonymous
// function call.
go func(msg string) {
fmt.Println(msg)
}("going")
// Our two function calls are running asynchronously in
// separate goroutines now. Wait for them to finish
// (for a more robust approach, use a [WaitGroup](waitgroups)).
time.Sleep(time.Second)
fmt.Println("done")
}

View File

@ -0,0 +1,2 @@
3737e7b5129b649d202e75225a1ac732eda116d0
rovAFf9-n78

View File

@ -0,0 +1,16 @@
# When we run this program, we see the output of the
# blocking call first, then the interleaved output of the
# two goroutines. This interleaving reflects the
# goroutines being run concurrently by the Go runtime.
$ go run goroutines.go
direct : 0
direct : 1
direct : 2
goroutine : 0
going
goroutine : 1
goroutine : 2
done
# Next we'll look at a complement to goroutines in
# concurrent Go programs: channels.

View File

@ -0,0 +1,9 @@
// Our first program will print the classic "hello world"
// message. Here's the full source code.
package main
import "fmt"
func main() {
fmt.Println("hello world")
}

View File

@ -0,0 +1,2 @@
c98395a44701add5bf84e2f3a63e300fc1bc4bfe
mp1ENMU6ZYu

View File

@ -0,0 +1,17 @@
# To run the program, put the code in `hello-world.go` and
# use `go run`.
$ go run hello-world.go
hello world
# Sometimes we'll want to build our programs into
# binaries. We can do this using `go build`.
$ go build hello-world.go
$ ls
hello-world hello-world.go
# We can then execute the built binary directly.
$ ./hello-world
hello world
# Now that we can run and build basic Go programs, let's
# learn more about the language.

View File

@ -0,0 +1,38 @@
// The Go standard library comes with excellent support
// for HTTP clients and servers in the `net/http`
// package. In this example we'll use it to issue simple
// HTTP requests.
package main
import (
"bufio"
"fmt"
"net/http"
)
func main() {
// Issue an HTTP GET request to a server. `http.Get` is a
// convenient shortcut around creating an `http.Client`
// object and calling its `Get` method; it uses the
// `http.DefaultClient` object which has useful default
// settings.
resp, err := http.Get("http://gobyexample.com")
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Print the HTTP response status.
fmt.Println("Response status:", resp.Status)
// Print the first 5 lines of the response body.
scanner := bufio.NewScanner(resp.Body)
for i := 0; scanner.Scan() && i < 5; i++ {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,2 @@
ec8fd69aa19e54a7ea05d2a911f09d3a98f0396c
VxYIifr_UuH

View File

@ -0,0 +1,7 @@
$ go run http-clients.go
Response status: 200 OK
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Go by Example</title>

View File

@ -0,0 +1,50 @@
// Writing a basic HTTP server is easy using the
// `net/http` package.
package main
import (
"fmt"
"net/http"
)
// A fundamental concept in `net/http` servers is
// *handlers*. A handler is an object implementing the
// `http.Handler` interface. A common way to write
// a handler is by using the `http.HandlerFunc` adapter
// on functions with the appropriate signature.
func hello(w http.ResponseWriter, req *http.Request) {
// Functions serving as handlers take a
// `http.ResponseWriter` and a `http.Request` as
// arguments. The response writer is used to fill in the
// HTTP response. Here our simple response is just
// "hello\n".
fmt.Fprintf(w, "hello\n")
}
func headers(w http.ResponseWriter, req *http.Request) {
// This handler does something a little more
// sophisticated by reading all the HTTP request
// headers and echoing them into the response body.
for name, headers := range req.Header {
for _, h := range headers {
fmt.Fprintf(w, "%v: %v\n", name, h)
}
}
}
func main() {
// We register our handlers on server routes using the
// `http.HandleFunc` convenience function. It sets up
// the *default router* in the `net/http` package and
// takes a function as an argument.
http.HandleFunc("/hello", hello)
http.HandleFunc("/headers", headers)
// Finally, we call the `ListenAndServe` with the port
// and a handler. `nil` tells it to use the default
// router we've just set up.
http.ListenAndServe(":8090", nil)
}

View File

@ -0,0 +1,2 @@
a4e8d30b7a6f3a6abd96b916d81ce5930bad94f9
lNuS9ysZmxH

View File

@ -0,0 +1,6 @@
# Run the server in the background.
$ go run http-servers.go &
# Access the `/hello` route.
$ curl localhost:8090/hello
hello

35
go/if-else/if-else.go Normal file
View File

@ -0,0 +1,35 @@
// Branching with `if` and `else` in Go is
// straight-forward.
package main
import "fmt"
func main() {
// Here's a basic example.
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
// You can have an `if` statement without an else.
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
// A statement can precede conditionals; any variables
// declared in this statement are available in all
// branches.
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}
// Note that you don't need parentheses around conditions
// in Go, but that the braces are required.

2
go/if-else/if-else.hash Normal file
View File

@ -0,0 +1,2 @@
89b78f3378e1a574ddfd0260a0404a962852eff8
p6-WKTqEks4

8
go/if-else/if-else.sh Normal file
View File

@ -0,0 +1,8 @@
$ go run if-else.go
7 is odd
8 is divisible by 4
9 has 1 digit
# There is no [ternary if](http://en.wikipedia.org/wiki/%3F:)
# in Go, so you'll need to use a full `if` statement even
# for basic conditions.

View File

@ -0,0 +1,64 @@
// _Interfaces_ are named collections of method
// signatures.
package main
import (
"fmt"
"math"
)
// Here's a basic interface for geometric shapes.
type geometry interface {
area() float64
perim() float64
}
// For our example we'll implement this interface on
// `rect` and `circle` types.
type rect struct {
width, height float64
}
type circle struct {
radius float64
}
// To implement an interface in Go, we just need to
// implement all the methods in the interface. Here we
// implement `geometry` on `rect`s.
func (r rect) area() float64 {
return r.width * r.height
}
func (r rect) perim() float64 {
return 2*r.width + 2*r.height
}
// The implementation for `circle`s.
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
return 2 * math.Pi * c.radius
}
// If a variable has an interface type, then we can call
// methods that are in the named interface. Here's a
// generic `measure` function taking advantage of this
// to work on any `geometry`.
func measure(g geometry) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
func main() {
r := rect{width: 3, height: 4}
c := circle{radius: 5}
// The `circle` and `rect` struct types both
// implement the `geometry` interface so we can use
// instances of
// these structs as arguments to `measure`.
measure(r)
measure(c)
}

View File

@ -0,0 +1,2 @@
aac1328f5a04568272b82753ff0762b9eacff4fc
hXTlbUAGcvn

View File

@ -0,0 +1,10 @@
$ go run interfaces.go
{3 4}
12
14
{5}
78.53981633974483
31.41592653589793
# To learn more about Go's interfaces, check out this
# [great blog post](http://jordanorelli.tumblr.com/post/32665860244/how-to-use-interfaces-in-go).

124
go/json/json.go Normal file
View File

@ -0,0 +1,124 @@
// Go offers built-in support for JSON encoding and
// decoding, including to and from built-in and custom
// data types.
package main
import (
"encoding/json"
"fmt"
"os"
)
// We'll use these two structs to demonstrate encoding and
// decoding of custom types below.
type response1 struct {
Page int
Fruits []string
}
// Only exported fields will be encoded/decoded in JSON.
// Fields must start with capital letters to be exported.
type response2 struct {
Page int `json:"page"`
Fruits []string `json:"fruits"`
}
func main() {
// First we'll look at encoding basic data types to
// JSON strings. Here are some examples for atomic
// values.
bolB, _ := json.Marshal(true)
fmt.Println(string(bolB))
intB, _ := json.Marshal(1)
fmt.Println(string(intB))
fltB, _ := json.Marshal(2.34)
fmt.Println(string(fltB))
strB, _ := json.Marshal("gopher")
fmt.Println(string(strB))
// And here are some for slices and maps, which encode
// to JSON arrays and objects as you'd expect.
slcD := []string{"apple", "peach", "pear"}
slcB, _ := json.Marshal(slcD)
fmt.Println(string(slcB))
mapD := map[string]int{"apple": 5, "lettuce": 7}
mapB, _ := json.Marshal(mapD)
fmt.Println(string(mapB))
// The JSON package can automatically encode your
// custom data types. It will only include exported
// fields in the encoded output and will by default
// use those names as the JSON keys.
res1D := &response1{
Page: 1,
Fruits: []string{"apple", "peach", "pear"}}
res1B, _ := json.Marshal(res1D)
fmt.Println(string(res1B))
// You can use tags on struct field declarations
// to customize the encoded JSON key names. Check the
// definition of `response2` above to see an example
// of such tags.
res2D := &response2{
Page: 1,
Fruits: []string{"apple", "peach", "pear"}}
res2B, _ := json.Marshal(res2D)
fmt.Println(string(res2B))
// Now let's look at decoding JSON data into Go
// values. Here's an example for a generic data
// structure.
byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
// We need to provide a variable where the JSON
// package can put the decoded data. This
// `map[string]interface{}` will hold a map of strings
// to arbitrary data types.
var dat map[string]interface{}
// Here's the actual decoding, and a check for
// associated errors.
if err := json.Unmarshal(byt, &dat); err != nil {
panic(err)
}
fmt.Println(dat)
// In order to use the values in the decoded map,
// we'll need to convert them to their appropriate type.
// For example here we convert the value in `num` to
// the expected `float64` type.
num := dat["num"].(float64)
fmt.Println(num)
// Accessing nested data requires a series of
// conversions.
strs := dat["strs"].([]interface{})
str1 := strs[0].(string)
fmt.Println(str1)
// We can also decode JSON into custom data types.
// This has the advantages of adding additional
// type-safety to our programs and eliminating the
// need for type assertions when accessing the decoded
// data.
str := `{"page": 1, "fruits": ["apple", "peach"]}`
res := response2{}
json.Unmarshal([]byte(str), &res)
fmt.Println(res)
fmt.Println(res.Fruits[0])
// In the examples above we always used bytes and
// strings as intermediates between the data and
// JSON representation on standard out. We can also
// stream JSON encodings directly to `os.Writer`s like
// `os.Stdout` or even HTTP response bodies.
enc := json.NewEncoder(os.Stdout)
d := map[string]int{"apple": 5, "lettuce": 7}
enc.Encode(d)
}

2
go/json/json.hash Normal file
View File

@ -0,0 +1,2 @@
c751bc7223b8bc615f82fe7643ab98ce2b80240f
ROikmz5tRhZ

21
go/json/json.sh Normal file
View File

@ -0,0 +1,21 @@
$ go run json.go
true
1
2.34
"gopher"
["apple","peach","pear"]
{"apple":5,"lettuce":7}
{"Page":1,"Fruits":["apple","peach","pear"]}
{"page":1,"fruits":["apple","peach","pear"]}
map[num:6.13 strs:[a b]]
6.13
a
{1 [apple peach]}
apple
{"apple":5,"lettuce":7}
# We've covered the basic of JSON in Go here, but check
# out the [JSON and Go](http://blog.golang.org/2011/01/json-and-go.html)
# blog post and [JSON package docs](http://golang.org/pkg/encoding/json/)
# for more.

View File

@ -0,0 +1,41 @@
// A _line filter_ is a common type of program that reads
// input on stdin, processes it, and then prints some
// derived result to stdout. `grep` and `sed` are common
// line filters.
// Here's an example line filter in Go that writes a
// capitalized version of all input text. You can use this
// pattern to write your own Go line filters.
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// Wrapping the unbuffered `os.Stdin` with a buffered
// scanner gives us a convenient `Scan` method that
// advances the scanner to the next token; which is
// the next line in the default scanner.
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
// `Text` returns the current token, here the next line,
// from the input.
ucl := strings.ToUpper(scanner.Text())
// Write out the uppercased line.
fmt.Println(ucl)
}
// Check for errors during `Scan`. End of file is
// expected and not reported by `Scan` as an error.
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}

View File

@ -0,0 +1,2 @@
87f4a67edf741979f8ff6da85947aa177547f9ef
hnaOIaQAjKF

View File

@ -0,0 +1,9 @@
# To try out our line filter, first make a file with a few
# lowercase lines.
$ echo 'hello' > /tmp/lines
$ echo 'filter' >> /tmp/lines
# Then use the line filter to get uppercase lines.
$ cat /tmp/lines | go run line-filters.go
HELLO
FILTER

50
go/maps/maps.go Normal file
View File

@ -0,0 +1,50 @@
// _Maps_ are Go's built-in [associative data type](http://en.wikipedia.org/wiki/Associative_array)
// (sometimes called _hashes_ or _dicts_ in other languages).
package main
import "fmt"
func main() {
// To create an empty map, use the builtin `make`:
// `make(map[key-type]val-type)`.
m := make(map[string]int)
// Set key/value pairs using typical `name[key] = val`
// syntax.
m["k1"] = 7
m["k2"] = 13
// Printing a map with e.g. `fmt.Println` will show all of
// its key/value pairs.
fmt.Println("map:", m)
// Get a value for a key with `name[key]`.
v1 := m["k1"]
fmt.Println("v1: ", v1)
// The builtin `len` returns the number of key/value
// pairs when called on a map.
fmt.Println("len:", len(m))
// The builtin `delete` removes key/value pairs from
// a map.
delete(m, "k2")
fmt.Println("map:", m)
// The optional second return value when getting a
// value from a map indicates if the key was present
// in the map. This can be used to disambiguate
// between missing keys and keys with zero values
// like `0` or `""`. Here we didn't need the value
// itself, so we ignored it with the _blank identifier_
// `_`.
_, prs := m["k2"]
fmt.Println("prs:", prs)
// You can also declare and initialize a new map in
// the same line with this syntax.
n := map[string]int{"foo": 1, "bar": 2}
fmt.Println("map:", n)
}

2
go/maps/maps.hash Normal file
View File

@ -0,0 +1,2 @@
3e39d07e3f80ecbac558c6fb8baee2a5f914cf97
U67R66Oab8r

9
go/maps/maps.sh Normal file
View File

@ -0,0 +1,9 @@
# Note that maps appear in the form `map[k:v k:v]` when
# printed with `fmt.Println`.
$ go run maps.go
map: map[k1:7 k2:13]
v1: 7
len: 2
map: map[k1:7]
prs: false
map: map[bar:2 foo:1]

Some files were not shown because too many files have changed in this diff Show More