Golang 基本语法速查——变量、控制结构与函数

一、基本结构

hello world 程序:

1
2
3
4
5
6
7
package main

import "fmt"

func main(){
fmt.Println("你好,世界")
}

直接运行:

1
2
$ go run hello.go
你好,世界

构建可执行程序:

1
2
3
$ go build hello.go
$ ./hello
你好,世界

PS

  • Package 用于组织代码架构,类似于其他语言中的命名空间
  • 每个 go 源文件都必须属于(且只属于一个)特定的 package,一个 package 可以包含多个 go 代码文件
  • import 导入某个 package ,意味着获取了该 package 中可见对象的访问权限
  • import 导入的 package 未在后面的代码中使用会报错,no unnecessary code! 原则
  • main 函数没有任何参数且不返回任何值,它通常作为程序执行的入口

导入多个包:

1
2
3
4
import (
"fmt"
"os"
)

二、变量

变量定义和赋值:var identifier [type] = value

1
2
3
4
5
var a int
var b bool
var str string = "hello"
var i = 5
GOROOT := os.Getenv("GOROOT")

PS

  • b := false 等同于 var b = false 。借助 Go 语言的类型推断可以省略类型声明
  • := 或者 var 形式的变量赋值,包含变量初始化动作。即 a = 1 表示将 1 赋值给变量 a(a 必须已经创建),a := 1 表示新建变量 a,并将 1 赋值给 a。
  • 使用未定义的变量,定义的本地变量未在后续代码中使用,新建变量的名称已存在等都会报错
  • 推荐使用 := 的形式,但只应该在函数内部使用

三、字符串

连接字符串,+

1
2
3
s := "hel" + "lo"
s += " world"
fmt.Println(s) // prints out "hello world"

  • 前缀匹配:strings.HasPrefix(s, prefix string) bool
  • 后缀匹配:strings.HasSuffix(s, suffix string) bool
  • 包含关系:strings.Contains(s, substr string) bool
  • 字符串替换:strings.Replace(str, old, new, n) string
  • 计算子字符串出现次数:strings.Count(s, str string) int
  • 分割字符串:strings.Split(s, sep string) []string
  • join 字符串:strings.join(sl []string, sep string) string

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
"strings"
)

func main() {
str := "The quick brown fox jumps over the lazy dog"
sl := strings.Split(str, " ")
fmt.Printf("Splitted in slice: %v\n", sl)
// 字符串分割后的返回值 sl 是数组
for _, val := range sl {
fmt.Printf("%s,", val)
}

fmt.Println()
str2 := strings.Join(sl, "/")
fmt.Printf("sl joined by / is: %s\n", str2)
}
// output
// Splitted in slice: [The quick brown fox jumps over the lazy dog]
// The,quick,brown,fox,jumps,over,the,lazy,dog,
// sl joined by / is: The/quick/brown/fox/jumps/over/the/lazy/dog

四、控制结构

if-else

if-else 结构语法如下:

1
2
3
4
5
6
7
if condition1 {
// do something
} else if condition2 {
// do something
} else {
// catch-all or default
}

PSelse 旁边的 }{ 不能隔行,否则无法编译通过。

switch
1
2
3
4
5
6
7
8
switch var1 {
case val1:
// do something
case val2:
// do something
default:
// catch-all or default
}
1
2
3
4
5
6
7
8
switch {
case condition1:
// do something
case condition2:
// do something
default:
// catch-all or default
}
1
2
3
4
5
6
7
8
switch initialization {
case val1:
// do something
case val2:
// do something
default:
// catch-all or default
}

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
)

func Abs(x int) int {
if x < 0 {
return -x
}
return x
}

func main() {
switch num := Abs(-5); {
case num > 0:
fmt.Println("Result is positive")
case num < 0:
fmt.Println("Never gonna happen")
default:
fmt.Println("Exact 0")
}
}
// Result is positive

for

参考如下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
)

func main() {
str := "Go is a beautiful language"

for ix :=0; ix < len(str); ix ++ {
fmt.Printf("Character on position %d is: %c \n", ix, str[ix])
}

for pos, char := range str {
fmt.Printf("Character on position %d is: %c \n", pos, char)
}
}

五、函数

定义函数语法:

1
2
func g() {
}

注意不能写成如下形式:

1
2
3
func g()
{
}

函数调用语法:pack1.Function(arg1,arg2, ... ,argn)

Go 中的函数可以接收另一个函数作为参数,比如有下面两个函数:
f1(a, b, c int) 接收三个参数
f2(a, b int) (int, int, int) 有两个参数和三个返回值
则可以这样调用:f1(f2(a, b))

函数本身可以赋值给变量,如 add := binOp

参数与返回值

Go 中的函数可以接收 0 个或多个参数,返回 0 个或多个结果。
传给函数的参数通常会有名称,但是也可以没有名称只包含类型:func f(int, int, float64)

Go 函数的传参默认是按值传递,即实际传给函数的是参数值的复制。如调用 Function(arg1),函数 Function 收到的是 arg1 指向的值的副本,而不是 arg1 本身。
如果需要函数操作传入的参数本身,即 Function(arg1) 操作 arg1 本体的值,则需要传入参数的指针(内存地址)。使用如下形式:Function(&arg1)

大部分函数都需要返回值,可以是命名的也可以不命名。

数量未知的参数

函数的最后一个参数可以是 ...type 这样的形式,即 func myFunc(a, b, arg ...int) {} 。这表示函数可以接收相同类型的不定数量的参数。参考如下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"fmt"
)

func main() {
x := Min(1, 3, 2, 0)
fmt.Printf("The minimum is: %d\n", x)

arr := []int{7, 9, 3, 5, 1}
x = Min(arr...)
fmt.Printf("The minimum in the array arr is: %d", x)
}

func Min(a ...int) int {
if len(a) == 0 {
return 0
}
min := a[0]
for _, v := range a {
if v < min {
min = v
}
}
return min
}
// The minimum is: 0
// The minimum in the array arr is: 1

Defer

defer 关键字用于“延迟”执行某个函数或命令,由 defer 修饰的语句会在外部函数返回结果之后再执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
)

func main() {
fmt.Printf("In Main function at the top\n")
defer Function2()
fmt.Printf("In Main function at the bottom\n")
}

func Function2() {
fmt.Printf("Function2: Deferred until the end of the calling function")
}
// In Main function at the top
// In Main function at the bottom
// Function2: Deferred until the end of the calling function%

如果 defer 语句中包含变量参数,则该参数的值在 defer 的位置已经确定。参考如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"
)

func main() {
i := 0
defer fmt.Println(i)
i++
fmt.Println("The number is:")
}
// The number is:
// 0

defer 修饰的 Println 函数虽然在程序执行流的最后输出 i 值。但变量 idefer 处的值为 0,因此最后输出 0 而不是执行 i++ 后的 1 。

当代码中包含有多个 defer 语句时,这些语句会以 LIFO(后进先出,类似 stack)的顺序执行。

1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
)

func main() {
for i := 0; i < 5; i++ {
defer fmt.Printf("%d", i)
}
}
// 43210

递归函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
)

func main() {
result := 0
for i := 0; i <= 10; i++ {
result = fibonacci(i)
fmt.Printf("fibonacci(%d) is: %d\n", i, result)
}
}

func fibonacci(n int) (res int) {
if n <= 1 {
res = 1
} else {
res = fibonacci(n-1) + fibonacci(n-2)
}
return
}
高阶函数

函数作为参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
callback(1, Add)
}

func Add(a, b int) {
fmt.Printf("The sum of %d and %d is: %d\n", a, b, a+b)
}

func callback(y int, f func(int, int)) {
f(y, 2)
}
// => The sum of 1 and 2 is: 3

(匿名)函数赋值给变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
for i := 0; i < 4; i++ {
g := func(i int) { fmt.Printf("%d", i) }
g(i)
fmt.Printf(" - g is of type %T and has value %v\n", g, g)
}
}
// => 0 - g is of type func(int) and has value 0x4839d0
// => 1 - g is of type func(int) and has value 0x4839d0
// => 2 - g is of type func(int) and has value 0x4839d0
// => 3 - g is of type func(int) and has value 0x4839d0

函数作为返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

func main() {
p2 := Add2()
fmt.Printf("Call Add2 for 3 returns: %v\n", p2(3))

TwoAdder := Adder(2)
fmt.Printf("The result is: %v\n", TwoAdder(3))
}

func Add2() func(b int) int {
return func(b int) int {
return b + 2
}
}

func Adder(a int) func(b int) int {
return func(b int) int {
return a + b
}
}
// => Call Add2 for 3 returns: 5
// => The result is: 5

另一个版本的累加器,涉及到闭包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
f := Adder()
fmt.Print(f(1), " - ")
fmt.Print(f(20), " - ")
fmt.Print(f(300))
}

func Adder() func(int) int {
var x int
return func(delta int) int {
x += delta
return x
}
}
// => 1 - 21 - 321

参考资料

The Way To Go: A Thorough Introduction To The Go Programming Language