20180319:TP:Go:Introduction
Sommaire
Introduction
This is the introductory session on Go. We'll start with some setup and then write some tiny programs.
Setup
Directory Architecture
Unlike C, Go requires some directory setup. Go expect an architecture containing your sources, packages and binaries. Go could be able to manage versioning through git directly, but it's not what we want here.
So, what we will do is add the main go directory in your git repos, prevent bin and pkg sub-directory to be added to your git and in the source dir (src) add the directories for each tutorial.
So what we expect is the following architecture under your git repos:
- go/
- src/
- Tutorial sub-directories
- pkg/
- bin/
- src/
Once your directories are created, you will add some entries to your .gitignore (can be at the root of your repos or directly in the go directory) to prevent versioning of pkg and bin:
bin/ pkg/
And finally, you'll set the environment variable GOPATH to the go directory.
Go app and packages
Go programs organization and naming follows some rules. If you got a main package (more in the lecture on that subject) in the path ${GOPATH}/src/my_app/foo.go the produced binary will be my_app (not foo.)
For this session, we will only build standalone program (single file main package) so we don't need to worry more about that.
Hello World
- Path: go/src/tutorial20180319/hello
This exercise is just a warm-up to test your setup, you will create (in the described path) a go file (you can name it as you want) with the following code:
package main import "fmt" func main() { fmt.Println("Hello, World !") }
You can now test your code:
# Commands that work even without a correct GOPATH # hello.go is supposed to be the name of your go file shell> go run hello.go Hello, World ! shell> go build hello.go shell> ./hello Hello, World ! shell> go clean # These will require a correct GOPATH shell> go build shell> ./hello Hello, World ! shell> go clean shell> go install shell> ${GOPATH}/bin/hello Hello, World ! # remove produced AND installed files shell> go clean -i
Code formatting and documentation
Go provide a formatter for source code using the command go fmt, you can try to mess up a little bit with indentation, run go fmt<tt> and look back at your code to see the change.
Go is also able to extract documentation from your code, we add some code to our Hello, world example:
/* hello: my first go program */ package main import ( "fmt" "math/rand" ) // MyFun(n): prints n and returns it func MyFun(n int) int { fmt.Println("An int: ", n) return n } func main() { fmt.Println("Hello, world") n := MyFun(rand.Int()) fmt.Println("returned n: ", n) }
You can now test the <tt>go doc command:
shell> go doc hello: my first go program shell> go doc MyFun func MyFun(n int) int MyFun(n): prints n and returns it shell> go doc -cmd package main // import "tutorials/introduction/hello" hello: my first go program func MyFun(n int) int shell>
Note: the import printed by go doc is related to my own directory, in your case you'll get the a path corresponding to your go source dir.
Let's go
In the next parts, you'll have to write some program (main package) with classical basic algorithms.
Factorial
- Path: go/src/tutorial20180319/factorial
Our goal is to implement the classic factorial function, I provided some helper code in order to test your function that you can reuse with other program.
- Complete the following code:
/* factorial: a simple factorial demo */ package main import ( "fmt" "log" "os" "strconv" ) // Fact(n): compute n! func Fact(n int) int { // FIX ME } func main() { if len(os.Args) < 2 { log.Fatalln("missing arguments") } n, err := strconv.ParseInt(os.Args[1], 10, 32) if err != nil || n < 0 { log.Fatalf("argument %v is not a proper positive integer", os.Args[1]) } fmt.Printf("Fact(%v) = %v\n", n, Fact(int(n))) }
Fibonacci
- Path: go/src/tutorial20180319/fibo
Our goal is to implement an efficient Fibonacci function, but in order to change a little bit, we'll see a linear recursive version using tuple return value. Here is the python implementation:
def _fibo(n): if n == 0: return 0,1 if n <= 2: return 1,1 a, b = _fibo(n - 1) return a + b, a def fibo(n): r, _ = _fibo(n) return r
- Implement in go the previous algorithm with a main function taking the input value on the command line.
Binary Search
- Path: go/src/tutorial20180319/bin_search
The provided code for this exercise shows some more standard go packages, here take a look at the command line flag management in the provided code.
// bin_search.go: simple binary search in sorted array of integer. package main import ( "flag" "fmt" "math/rand" ) // BinSearch(tab, x) search for x in tab using efficient binary searching // algorithm // Returns a pair made of a position and a boolean, the boolean is false if x // is not in the array, true otherwise. func BinSearch(tab []int, x int) (int, bool) { // ** FIX ME *** } // genSortedArray(size) build a random sorted array of len size func genSortedArray(size int) []int { tab := make([]int, size) cur := rand.Intn(32) for i := 0; i < size; i++ { tab[i] = cur cur += rand.Intn(16) + 1 } return tab } // bin_search: test binary searching func main() { var size int var seed int64 // mandatory for rand.Seed() // Flags // ** FIX ME ** // take 2 flags: size and seed setting the value for variable size and seed // use flag.IntVar() et flag.Int64Var() // use some reasonable default values for size and seed. // see https://golang.org/pkg/flag // Init random generator rand.Seed(seed) tab := genSortedArray(size) fmt.Println(tab) pos := rand.Intn(size) x := tab[pos] fmt.Println("\nSearch for existing element:", x) pos_res, found := BinSearch(tab, x) fmt.Printf("BinSearch(tab, x): (%v, %v)\n", pos_res, found) x = tab[0] + rand.Intn(tab[len(tab)-1]-tab[0]) fmt.Println("\nSearch for random element:", x) pos_res, found = BinSearch(tab, x) fmt.Printf("BinSearch(tab, x): (%v, %v)\n", pos_res, found) }
- Complete the previous code
In order to complete the part about flags, here is a simple tiny program using the flag package:
// flagex [-flag int] [-help|--help|-h] // // flagex takes a flag (default value 0) and displays it. package main import ( "flag" "fmt" ) func main() { var f int flag.IntVar(&f, "flag", 0, "a simple integer flag") flag.Parse() fmt.Println("flag:", f) }