基本语法
Hello world
hello.go
package main import "fmt" func main() { message := greetMe("world") fmt.Println(message) } func greetMe(name string) string { return "Hello, " + name + "!" }
go build
基本类型
数字
典型数字类型:
num := 3 // int num := 3. // float64 num := 3 + 4i // complex128 num := byte('a') // byte (alias for uint8)
其他数字类型:
var u uint = 7 // uint (unsigned) var p float32 = 22.7 // 32-bit float
指针
func main () { b := *getPointer() fmt.Println("Value is", b) } func getPointer () (myPointer *int) { a := 234 return &a }
指针指向变量的内存地址。Go is fully garbage-collected.
类型转换
i := 2 f := float64(i) u := uint(i)
流程控制
条件
if day == "sunday" || day == "saturday" { rest() } else if day == "monday" && isTired() { groan() } else { work() }
if 内语句
if _, err := getResult(); err != nil { fmt.Println("Uh oh") }
在 ; 之前的 if 中的语句(即 _, err := getResult() )可以被提前处理。
switch
switch day { case "sunday": // cases don't "fall through" by default! // case 默认是不会 fall through 的 fallthrough case "saturday": rest() default: work() }
For 循环
for count := 0; count <= 10; count++ { fmt.Println("My counter is at", count) }
For-Range 循环
entry := []string{"Jack","John","Jones"} for i, val := range entry { fmt.Printf("At position %d, the character %s is present\n", i, val) }
函数
多返回值
a, b := getMessage() func getMessage() (a string, b string) { return "Hello", "World" }
具名返回值
func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return }
如果在签名中定义返回值名,return(无参数) 将返回那些具名变量。
包
importing(包引入)
import "fmt" import "math/rand" import ( "fmt" // gives fmt.Println "math/rand" // gives rand.Intn )
上述两种写法是一样的。
Aliases 包别名
import r "math/rand" r.Intn()
并发性
Goroutines
func main() { // A "channel" ch := make(chan string) // Start concurrent routines go push("Moe", ch) go push("Larry", ch) go push("Curly", ch) // Read 3 results // (Since our goroutines are concurrent, // the order isn't guaranteed!) // 因为 goroutines 是并发的,所以不保证顺序。 fmt.Println(<-ch, <-ch, <-ch) } func push(name string, ch chan string) { msg := "Hey, " + name ch <- msg }
Channels 被用于 goroutine 之间,是并发安全的通信对象。
Buffered channels
ch := make(chan int, 2) ch <- 1 ch <- 2 ch <- 3 // fatal error: // all goroutines are asleep - deadlock!
Buffered channel 限定了可以保存消息的数量。
Closing channels(关闭channel)
关闭一个channel:
ch <- 1 ch <- 2 ch <- 3 close(ch)
在 channel 上遍历直到关闭:
for i := range ch { //··· }
如果 ok 是 false 的话 channel 就是关闭的。
v, ok := <- ch
WaitGroup
import "sync" func main() { var wg sync.WaitGroup for _, item := range itemList { // Increment WaitGroup Counter wg.Add(1) go doOperation(item) } // Wait for goroutines to finish wg.Wait() } func doOperation(item string) { defer wg.Done() // do operation on item // ... }
WaitGroup 等待一组 goroutines 结束。main goroutine 调用 Add 来设置需要等待的 goroutines 数量。 goroutine 结束时调用 wg.Done()。
错误处理
Defer
func main() { defer fmt.Println("Done") fmt.Println("Working...") }
Defers running a function until the surrounding function returns. The arguments are evaluated immediately, but the function call is not ran until later.
参数是立刻被传入的,但是函数调用发生在之后。比如👇[r1]这里如果不传入指针输出会是什么?
Deferring functions
func main() { defer func() { fmt.Println("Done") }() fmt.Println("Working...") }
Lambdas 更适合 defer blocks。👆[r1]
func main() { var d = int64(0) defer func(d *int64) { fmt.Printf("& %v Unix Sec\n", *d) }(&d) fmt.Print("Done ") d = time.Now().Unix() }
The defer func uses current value of d, unless we use a pointer to get final value at end of main.
Structs
定义
type Vertex struct { X int Y int } func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X, v.Y) }
Literals(字面量)
v := Vertex{X: 1, Y: 2} // Field names can be omitted // 段名可以不写 v := Vertex{1, 2} // Y is implicit v := Vertex{X: 1}
Methods(方法)
Receivers
type Vertex struct { X, Y float64 } func (v Vertex) Abs() float64 { return math.Sqrt(v.X * v.X + v.Y * v.Y) } v: = Vertex{1, 2} v.Abs()
没有类,但是可以用 receivers 定义函数。
Mutation(突变)
func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f } v := Vertex{6, 12} v.Scale(0.5) // `v` is updated
通过把 receiver 定义为 指针 (*Vertex) 就能做突变(改变结构体中字段值)了。
其他
package main import ( "fmt" "math" ) /* 结构体 */ type person struct { name string age int } /* 方法 */ type rect struct { width, height int } func (r *rect) area() int { return r.width * r.height } func (r rect) perim() int { return 2*r.width + 2*r.height } /* Interface */ type geometry interface { area() float64 perim() float64 } type square struct { sideLen float64 } type circle struct { radius float64 } func (s square) area() float64 { return s.sideLen*s.sideLen } func (s square) perim() float64 { return 4*s.sideLen } func (c circle) area() float64 { return math.Pi * c.radius * c.radius } func (c circle) perim() float64 { return 2 * math.Pi * c.radius } func measure(g geometry) { fmt.Println(g) fmt.Println(g.area()) fmt.Println(g.perim()) } func main() { /* 类型判断 */ whatAmI := func(i interface{}) { switch t := i.(type) { case bool: fmt.Println("I'm a bool") case int: fmt.Println("I'm an int") default: fmt.Printf("Don't know type %T\n", t) } } whatAmI(true) whatAmI(1) whatAmI("hey") /* 函数变长参数 */ sum(1, 2) sum(1, 2, 3) nums := []int{1, 2, 3, 4} sum(nums...) /* 闭包 */ nextInt := intSeq() fmt.Println(nextInt()) fmt.Println(nextInt()) fmt.Println(nextInt()) newInts := intSeq() fmt.Println(newInts()) /* 指针 */ i := 1 fmt.Println("initial:", i) zeroval(i) fmt.Println("zeroval:", i) zeroptr(&i) fmt.Println("zeroptr:", i) fmt.Println("pointer:", &i) /* 结构体 */ s := person{name: "Sean", age: 50} fmt.Println(s.name) sp := &s fmt.Println(sp.age) /* 方法 */ r := rect{width: 10, height: 5} fmt.Println("area: ", r.area()) fmt.Println("perim:", r.perim()) rp := &r fmt.Println("area: ", rp.area()) fmt.Println("perim:", rp.perim()) /* Interface */ sq := square{sideLen:3} c := circle{radius:5} measure(sq) measure(c) } /* 函数变长参数 */ func sum(nums ...int) { fmt.Print(nums, " ") total := 0 for _, num := range nums { total += num } fmt.Println(total) } /* 闭包 */ func intSeq() func() int { i := 0 return func() int { i++ return i } } /* 指针 */ func zeroval(ival int) { ival = 0 } func zeroptr(iptr *int) { *iptr = 0 } // https://blog.golang.org/go-slices-usage-and-internals // http://jordanorelli.tumblr.com/post/32665860244/how-to-use-interfaces-in-go
参考资料: https://gobyexample.com/
集合处理
下面的代码以 string 为例,说明了 golang 在集合处理方面的函数和一些惯用法:
package main import "strings" import "fmt" // 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)) }
主要值得学习里面的 golang 函数式编程的惯用法。