| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Notes for my Go programming workshop.
Go is very similar to Java
Java developers will learn Go very quickly.
Java:
Go:
Optional (not needed in this workshop): Set GOPATH to workspace, like $HOME/go.
go
go help
go help run
go help fmtThe go command is more like mvn than like javac.
go get ... <-- download from github, bitbucket, etc. go test ... <-- like 'mvn test' go install ... <-- install binary to $GOPATH/bin
Like mvn, the go command assumes a defined directory structure:
$GOPATH/bin <- compiled executables $GOPATH/pkg <- compiled libraries $GOPATH/src/github.com/... <- source code $GOPATH/src/bitbucket.org/... <- source code $GOPATH/src/... <- source code
In real-world go development, you run go install to compile the source code and create an executable in $GOPATH/bin.
However, in this workshop we use go run, which is a shortcut to quickly run a single go file. The go run command does not require the directory structure above.
package main
import (
"fmt"
)
func main() {
fmt.Printf("Hello, World!\n")
}go fmt hello.go
go run hello.goRead the documentation of the fmt package and the Printf call
go doc fmt
go doc fmt.Printfpackage main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage: ./hello <name>\n")
return
}
fmt.Printf("Hello, %v\n", os.Args[1])
}Use of for like Java's for
for i := 0; i < 10; i++ {
fmt.Printf("%v\n", i)
}Use of for like Java's while
i := 0
for i < 10 {
fmt.Printf("%v\n", i)
i++
}Use of for like Java's while(true)
i := 0
for {
if i >= 10 {
break
}
fmt.Printf("%v\n", i)
i++
}Write a program that says hello to multiple people:
> go run hello.go Fabian Thomas Christian
Hello, Fabian!
Hello, Thomas!
Hello, Christian!Variable declaration
var a int
a = 3Variable declaration with initializer
var a = 3Short variable declaration
a := 3Declaring multiple variables
var (
a, b int
c string
)
var a, b, c = 3, 4, "hello"Constants (like final in Java)
const a = 3func add(a int, b int) int {
return a + b
}func add(a, b int) int {
return a + b
}func addAndMult(a, b int) (int, int) {
return a + b, a * b
}
func main() {
sum, product := addAndMult(3, 7)
fmt.Printf("sum = %v, product = %v\n", sum, product)
}package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("world", "hello")
fmt.Printf("%v, %v\n", a, b)
}package main
import (
"fmt"
"strconv"
"os"
)
func main() {
// We assume that len(os.Args) >= 2
n, err := strconv.Atoi(os.Args[1])
if err != nil {
fmt.Printf("Error: %v is not a valid number.\n", os.Args[1])
return
}
fmt.Printf("Your number is %v\n", n)
}Go's nil is like Java's null.
In go it's a syntax error if a variable is declared but not used:
func main() {
sum, product := addAndMult(3, 7)
fmt.Printf("sum = %v\n", sum)
}Syntax error, because variable product is declared but not used.
To explicitly ignore a return value, use _:
func main() {
sum, _ := addAndMult(3, 7)
fmt.Printf("sum = %v\n", sum)
}Write a program that prints the first n Fibonacci numbers.
> go run fibonacci.go 10
1 1 2 3 5 8 13 21 34 55As a simple way to play with functions and loops, implement the square root function using Newton's method.
In this case, Newton's method is to approximate Sqrt(x) by picking a starting point z and then repeating:
To begin with, just repeat that calculation 10 times and see how close you get to the answer for various values (1, 2, 3, ...).
Next, change the loop condition to stop once the value has stopped changing (or only changes by a very small delta). See if that's more or fewer iterations. How close are you to the math.Sqrt?
Hint: to declare and initialize a floating point value, give it floating point syntax or use a conversion:
z := float64(1)
z := 1.0
package main
import (
"bufio"
"fmt"
"os"
)
const path = "./hello.txt"
func main() {
file, err := os.Open(path)
if err != nil {
fmt.Printf("failed to open file: %v\n", err)
os.Exit(-1)
}
defer file.Close()
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
fmt.Printf("%v", line)
}
}Modify the hello world program and add some deferred method calls, like
defer fmt.Printf("defer 1\n")
defer fmt.Printf("defer 2\n")
defer fmt.Printf("defer 3\n")Figure out the order in which these calls are executed.
package main
import "fmt"
func double(i int) {
i = i * 2
}
func main() {
i := 2
double(i)
fmt.Printf("i = %v\n", i)
}Result: i = 2
package main
import "fmt"
func double(i *int) {
*i = *i * 2
}
func main() {
i := 2
double(&i)
fmt.Printf("i = %v\n", i)
}Result: i = 4
package main
import "fmt"
type rectangle struct {
height float64
width float64
}
func main() {
square := &rectangle{
height: 4.0,
width: 4.0,
}
fmt.Printf("square has area of %v\n", square.height * square.width)
}package main
import "fmt"
type rectangle struct {
height float64
width float64
}
func (r *rectangle) area() float64 {
return r.width * r.height
}
func main() {
square := &rectangle{
height: 4.0,
width: 4.0,
}
fmt.Printf("square has area of %v\n", square.area())
}Define struct circle with variable radius that also has a method area().
Hint: go doc math.Pi
Java: Implicit conversion from float64 to String
return "the area is " + s.area();Go: Explicit string formatting
return fmt.Sprintf("area is %v", s.area())type shape interface {
area() float64
}
func areaInfo(s shape) string {
return fmt.Sprintf("area is %v", s.area())
}
func main() {
c := &circle{
radius: 2.0,
}
r := &rectangle{
width: 4.0,
height: 4.0,
}
fmt.Printf("circle: %v\n", areaInfo(c))
fmt.Printf("rectangle: %v\n", areaInfo(r))
}Go has some pre-defined interfaces. The most important ones are:
type Stringer interface {
String() string
}Like Java's toString()
type error interface {
Error() string
}Remember file, err := os.Open(path)? The err is of type error.
Implement a String() method for rectangle and circle.
The following code:
func main() {
c := &circle{
radius: 2.0,
}
r := &rectangle{
width: 4.0,
height: 4.0,
}
fmt.Printf("Shape 1: %v\n", c)
fmt.Printf("Shape 2: %v\n", r)
}Should produce the following output:
Shape 1: circle with radius 2 and area 12.566370614359172
Shape 2: rectangle with width 4, height 4, and area 16Example 1: Define an anonymous function and assign it to a variable
package main
import "fmt"
func main() {
f := func(n int) int {
return 2 * n
}
fmt.Printf("f(21) = %v\n", f(21))
}Example 2: Pass a function as parameter to another function
package main
import "fmt"
func applyFunction(n int, f func(int) int) {
fmt.Printf("f(%v) = %v\n", n, f(n))
}
func main() {
f := func(n int) int {
return 2 * n
}
applyFunction(21, f)
}Java Arrays:
int size = 10;
int[] arr = new int[size];Go Slices:
size := 10
slice := make([]int, size)Iterate over a slice:
for i, val := range slice {
fmt.Printf("slice[%v] = %v\n", i, val)
}package main
import "fmt"
func createSlice(size int) []int {
slice := make([]int, size)
for i := 0; i < size; i++ {
slice[i] = i + 1
}
return slice
}
func applyFunction(slice []int, f func(int) int) {
for i, n := range slice {
slice[i] = f(n)
}
}
func main() {
f := func(n int) int {
return 2 * n
}
slice := createSlice(10)
fmt.Printf("%v\n", slice)
applyFunction(slice, f)
fmt.Printf("%v\n", slice)
}Result:
[1 2 3 4 5 6 7 8 9 10]
[2 4 6 8 10 12 14 16 18 20]Example 1:
slice := createSlice(10)
fmt.Printf("%v\n", slice)
part := slice[3:7]
applyFunction(part, f)
fmt.Printf("%v\n", slice)Result:
[1 2 3 4 5 6 7 8 9 10]
[1 2 3 8 10 12 14 8 9 10]Example 2:
slice := createSlice(10)
fmt.Printf("%v\n", slice)
firstHalf := slice[:len(slice)/2]
applyFunction(firstHalf, f)
fmt.Printf("%v\n", slice)
secondHalf := slice[len(slice)/2:]
applyFunction(secondHalf, f)
fmt.Printf("%v\n", slice)Result:
[1 2 3 4 5 6 7 8 9 10]
[2 4 6 8 10 6 7 8 9 10]
[2 4 6 8 10 12 14 16 18 20]Write a function combine() such that
func main() {
add := func(a, b int) int {
return a + b
}
mult := func(a, b int) int {
return a * b
}
slice := createSlice(10)
fmt.Printf("add(1..10)=%v\n", combine(slice, add))
fmt.Printf("mult(1..10)=%v\n", combine(slice, mult))
}Prints the following output
add(1..10)=55
mult(1..10)=3628800Bonus task: Implement two versions of combine(): one with a for loop, and one recursive version without using for.
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Printf("%v\n", s)
}
}
func main() {
go say("world")
say("hello")
}package main
import (
"time"
"fmt"
)
func produceNumber(c chan int) {
time.Sleep(10 * time.Second)
c <- 3
}
func main() {
c := make(chan int)
go produceNumber(c)
n := <-c
fmt.Printf("n = %v\n", n)
}package main
import (
"time"
"fmt"
)
func produceNumber(c chan int) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
c <- i + 1
}
close(c)
}
func main() {
c := make(chan int)
go produceNumber(c)
for n := range c {
fmt.Printf("n = %v\n", n)
}
}Re-write the channel examples with two go-routines:
Terminology: A function running as a background thread is a go-routine.
package main
import (
"time"
"fmt"
)
func produceNumbers(c chan int) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
c <- 3
}
close(c)
}
func consumeNumbers(c chan int, done chan interface{}) {
for n := range c {
fmt.Printf("n = %v\n", n)
}
close(done)
}
func main() {
c := make(chan int)
done := make(chan interface{})
go produceNumbers(c)
go consumeNumbers(c, done)
<-done
}chan c int
chan done interface{}
select {
case n <- c:
// do something with n
case <- done:
// shut down
}Implement a deadlock (go-routine A is reading from channel of go-routine B and vice versa) and see what happens.
-> Always the producer should close a channel.
package main
import (
"fmt"
"net/http"
"os"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}
func main() {
http.HandleFunc("/", handler)
err := http.ListenAndServe("localhost:8000", nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to open Web server on localhost:8080: %v\n", err)
}
}Include a unique number in each response:
Avoid race conditions. Don't communicate through shared memory, use channels!!!
From The Go Programming Language, Chapter 1.7:
package main
import (
"fmt"
"log"
"net/http"
"sync"
)
var mu sync.Mutex
var count int
func main() {
http.HandleFunc("/", handler)
http.HandleFunc("/count", counter)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
// handler echoes the Path component of the requested URL.
func handler(w http.ResponseWriter, r *http.Request) {
mu.Lock()
count++
mu.Unlock()
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}
// counter echoes the number of calls so far.
func counter(w http.ResponseWriter, r *http.Request) {
mu.Lock()
fmt.Fprintf(w, "Count %d\n", count)
mu.Unlock()
}Topics covered in this workshop:
package main
import (
"fmt"
"os"
)
func main() {
for i := 1; i<len(os.Args); i++ {
fmt.Printf("Hello, %v\n", os.Args[i])
}
}package main
import (
"fmt"
"os"
"strconv"
)
func main() {
if len(os.Args) != 2 {
fmt.Print("Usage: fibonacci <n>\n")
return
}
n, err := strconv.Atoi(os.Args[1])
if err != nil {
fmt.Print("Invalid number.\n")
return
}
fmt.Print("Result:")
var prev, cur = 0, 1
for i := 0; i < n; i++ {
fmt.Printf(" %v", cur)
prev, cur = cur, prev + cur
}
fmt.Print("\n")
}package main
import (
"fmt"
)
func main() {
defer fmt.Printf("defer 1\n")
defer fmt.Printf("defer 2\n")
fmt.Printf("Hello, World!\n")
defer fmt.Printf("defer 3\n")
}package main
import (
"fmt"
"math"
)
type rectangle struct {
height float64
width float64
}
func (r *rectangle) area() float64 {
return r.width * r.height
}
type circle struct {
radius float64
}
func (c *circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func main() {
r := &rectangle{
height: 4.0,
width: 4.0,
}
c := &circle{
radius: 2.0,
}
fmt.Printf("square has area of %v\n", r.area())
fmt.Printf("circle has area of %v\n", c.area())
}package main
import (
"fmt"
"math"
)
type rectangle struct {
height float64
width float64
}
func (r *rectangle) area() float64 {
return r.width * r.height
}
type circle struct {
radius float64
}
func (c *circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (r *rectangle) String() string {
return fmt.Sprintf("rectangle with width %v, height %v, and area %v", r.width, r.height, r.area())
}
func (c *circle) String() string {
return fmt.Sprintf("circle with radius %v and area %v", c.radius, c.area())
}
func main() {
c := &circle{
radius: 2.0,
}
r := &rectangle{
width: 4.0,
height: 4.0,
}
fmt.Printf("Shape 1: %v\n", c)
fmt.Printf("Shape 2: %v\n", r)
}package main
import "fmt"
// for loop
func combine1(slice []int, f func(int, int) int) int {
result := slice[0]
for i := 1; i < len(slice); i++ {
result = f(result, slice[i])
}
return result
}
// recursive
func combine2(slice []int, f func(int, int) int) int {
if len(slice) == 1 {
return slice[0]
}
if len(slice) == 2 {
return f(slice[0], slice[1])
}
left := combine2(slice[:len(slice)/2], f)
right := combine2(slice[len(slice)/2:], f)
return f(left, right)
}
func createSlice(size int) []int {
slice := make([]int, size)
for i:=0; i<size; i++ {
slice[i] = i+1
}
return slice
}
func main() {
add := func(a, b int) int {
return a + b
}
mult := func(a, b int) int {
return a * b
}
slice := createSlice(10)
fmt.Printf("for loop:\n")
fmt.Printf("add(1..10)=%v\n", combine1(slice, add))
fmt.Printf("mult(1..10)=%v\n", combine1(slice, mult))
fmt.Printf("\nrecursive:\n")
fmt.Printf("add(1..10)=%v\n", combine2(slice, add))
fmt.Printf("mult(1..10)=%v\n", combine2(slice, mult))
}package main
import (
"time"
"fmt"
)
func produceNumbers(c chan int) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
c <- i
}
close(c)
}
func consumeNumbers(c chan int) {
for n := range c {
fmt.Printf("n = %v\n", n)
}
}
func main() {
c := make(chan int)
go produceNumbers(c)
go consumeNumbers(c)
time.Sleep(12 * time.Second)
}package main
func main() {
c1, c2 := make(chan int), make(chan int)
done := make(chan interface{})
go func() {
<-c1 // read from c1
c2 <- 42 // write to c2
close(done)
}()
go func() {
<-c2 // read from c2
c1 <- 42 // write to c1
<-done
}()
<-done
}package main
import (
"fmt"
"net/http"
"os"
)
var c = make(chan int)
func producer() {
i := 0
for {
i++
c <- i
}
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Request number is %v. Note that you browser might send two requests per 'reload'.\n", <- c)
}
func main() {
go producer()
http.HandleFunc("/", handler)
err := http.ListenAndServe("localhost:8000", nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to open Web server on localhost:8080: %v\n", err)
}
}| Back | FazBrowse Home | New Git URL |