基本语法

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

变量

Variable declaration(变量声明)

var msg string
msg = "Hello"

Infers type(类型推断,上面写法的简写)

msg := "Hello"

常量

const Phi = 1.618

常量可以是字符,字符串,布尔值,或者数值

基本类型

字符串

str := "Hello"

Strings 是 string 类型的。go 支持 多行字符串

数字

典型数字类型:

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

数组

// var numbers [5]int
numbers := [...]int{0, 0, 0, 0, 0}

数组是固定长度的。

切片

slice := []int{2, 3, 4}
slice := []byte("Hello")

不同于数组类型,切片类型是动态长度的。

指针

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

函数

Lambdas(λ)

myfunc := func() bool {
    return x > 10000
}

函数是一等类对象。

多返回值

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

Exporting names(导出)

func Hello () {
    //···
}

导出名以大写字母开头。

Packages (包)

package hello

每个包文件必须以 package 开始。

并发性

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}

struct指针

v := &Vertex{1, 2}
v.X = 2

当 v 是指针的时候 v.X 和 (*v).X 是一样的。

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 函数式编程的惯用法。

交叉编译

Go可以借助 go build 很方便得进行交叉编译。命令如下:

# ubuntu 下编译出 macos 和 windows 的可执行文件
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go

更多对应的组合在 go官方文档