Baiyangcao

A dobe Programmer

Follow me on GitHub

再跳一坑--Go

28 May 2017

小生又准备跳新坑了(无奈脸),假装自己是一个很好学的孩纸…

Go 程序由包构成,程序入库是 main 包,在程序开头定义文件所属的包

package main
// 这里定义当前程序为包 main

func main() {
    // 这里就是程序的入口啦~~~
}

在程序中使用其他包里的对象时,首先要导入包, 按照惯例,包名与导入的路径的最后一个目录一致

package main

import (
    // 导入 package fmt
    "fmt"
    // 多个包分行排列,注意行末没有逗号
    // 导入 math 路径下的 package rand
    "math/rand"
)

func main() {
    fmt.Println(rand.Intn)
}

在包中,首字母大写的对象会被导出,即在 import 包时, 可以访问到的对象只有包中首字母大写了的对象

函数

func add(x int, y int) int {
    return x + y
}

如上述,add为函数名, xy 为传入参数, 其后跟的是对应的参数类型 int, 而括号之后的 int 为返回值类型

在例子中,连续多个参数的类型相同可以只写最后一个参数的类型,如上参数列表可以缩写为 x, y int

在 Go 中,函数并不只能返回单一值,可以返回多个值,如下:

package main

import "fmt"

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    // 注意这里使用了 := 而不是 =
    a, b := swap("fuck", "world")
    fmt.Println(a, b)
}

Go 程序中的函数返回值可以被命名,并且作为变量在函数中使用, 而后使用没有参数的 return 语句,返回变量的当前值

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    // 返回 x, y 的当前值
    return
}

注:命名返回值只应该被运用在短函数中,在长函数中影响代码的可读性

变量

var (变量名) (变量类型), (变量名) (变量类型) ... = (变量值), (变量值)

// 如:
var a int = 1
var a, b int = 1, 2
var a bool, b string = true, "3"
var a, b = true, 3

// 变量在声明时没有赋值,则默认为 零值
// 数值为 0
var a int
// 布尔类型为 false
var b bool
// 字符串为 "" 空字符串
var c string 

在函数中, := 简洁赋值语句在明确类型的地方,可以用于替代 var 定义

func main() {
    var a, b = 1, 2
    k := 2
    c, d = true, "false"
}

注::= 结构仅能用在函数体之中

Go 程序之中必须要求显示的类型转换,转换函数即为对应的类型名称,如 float64

常量

常量使用 const 关键字来定义

const a = 1
const (
    a = 1
    b string = "test"
)

for 循环

for i:=0; i<10; i++ {
    // 循环体...
}

Go 程序中只有一种循环结构,for 循环, 其后的三个组成部分初始化语句,循环条件,后置语句不需要括号, 但是循环体必须要使用花括号

其中,初始化语句与后置语句可以省略,与 while 循环类似

for ; sum<1000; {
    sum += sum
}

或者省略分号,直接写成 for sum<1000 {}, 无限循环可以省略条件,直接写成 for {}

if 条件

if x > 0 {
    ...
}

if 语句中的条件不需要括号,而且可以在执行条件检查之前, 执行一条简单的语句,如赋值等

// 这里声明的变量只在 if...else... 语句范围内有效
if v := x * 3; v < lim {
    ...
} else {
    return v
}

switch 语句

// 与 if 类似,可以在判断条件前执行一个语句
switch name := "Han MeiMei"; name {
    case "Li Lei":
        fmt.Println("How are you?")
        // 这里分支回自动结束
    case "Han MeiMei":
        fmt.Println("I'm fine, thank you!")
        fmt.Println("And you?")
        // fallthrough 可以继续执行下一个 case 分支的语句
        fallthrough
    case f():
        ...
    default:
        fmt.Println("Hi!")
}

switch 的条件从上到下的执行,当匹配成功的时候停止。 上例中函数 f() 就不会执行

t := time.Now()
switch {
case t.Hour() < 12:
    fmt.Println("Good morning!")
case t.Hour() < 17:
    fmt.Println("Good afternoon.")
default:
    fmt.Println("Good evening.")
}

没有条件的 switch 可以用于替代 if-elif-else 结构

defer 语句

defer 语句会延迟函数的执行直到上层函数返回。 延迟调用的参数会立刻生成,但是在上层函数返回前函数都不会被调用。

package main

import "fmt"

func test(x string) string {
	fmt.Println("test function invoked")
	return x	
}

func main() {
	defer fmt.Println(test("world"))

	fmt.Println("hello")
}

这里会先执行 test 函数,计算出参数值, 然后输出 hello,最后输出 world, 对于同一个函数中多次调用 defer 语句的情况, 延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用。

// 会依次输出从 9 到 0
for i := 0; i < 10; i++ {
    defer fmt.Println(i)
}