20180319:TP:Go:Introduction

De wiki-prog
Aller à : navigation, rechercher


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/

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