Yefei.Blog

个人日记 WIKI

用户工具

站点工具


go:learn

Go 语言简要

包名规则

包导入使用的是文件中 package 定义的名称, 按照约定俗称包文件名和 package 中定义的名称一致

import "fmt"
import fmt2 "xxx/fmt" // 包导入名称如果有冲突可以使用此方法别名

包成员导出规则

包中导出的函数和变量开头为大写字母,小写字母开头的不导出(不可被外部调用)

  • func foobar 相当于 private func foobar
  • func Foobar 相当于 public func Foobar

函数

当两个或多个连续的函数命名参数是同一类型, 可以只写最后一个参数的类型,例如:

// 函数参数类型,和返回类型
func add(x int, y int) int {}
// 可以简写为
func add(x, y int) int {}
 
// 闭包
var plus = func(a, b int) int {
    return a + b
}
 
// 函数返回函数
func adder() func(int) int {
    sum := 0
    return func(x int) int {
        sum += x
        return sum
    }
}

多值返回

函数可以返回多个参数

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

命名返回值

函数的返回值可以命名

  • 直接和函数内变量绑定
  • 可以作为文档使用
func swap(x, y string) (a string, b string) {
    a, b = y, x
    return  // 直接返回即返回对应变量名称的值
}

变量&常量

变量定义的几种方式

var hello int = 100  // 变量类型在名称的后面
var hello = 100  // 根据值自动推导类型
hello := 100  // 使用 := 省去 var (但不能在函数外这么使用,函数外请老实使用 var 定义!)
var c, python, java bool // 像函数参数中一样,如果一组变量都是一样的类型可以只指定最后一个类型
_ // 特殊变量下划线,变量占位符,作为忽略变量使用

常量使用 const 关键字

变量定义时的默认值:

  • 数值类型为 0
  • 布尔类型为 false
  • 字符串为 “”(空字符串)

基本类型

类型定义文件在 src/builtin/builtin.go

bool
string
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的别名
rune // int32 的别名 代表一个Unicode码
float32 float64
complex64 complex128

类型转换

表达式 T(v) 将值 v 转换为类型 `T`

var i int = 42
var f float64 = float64(i)

for

Go 只有一种循环结构——`for` 循环

for i := 0; i < 10; i++ {
}
 
sum := 1
for sum < 10 {
    sum++
}
 
for {
}

if

if x > 100 {
}
 
// 跟 for 一样,`if` 语句可以在条件之前执行一个简单的语句, 作用域仅在 if 范围之内
if v := math.Pow(x, n); v < lim {
}

switch

switch 的条件从上到下的执行,当匹配成功的时候停止

switch os := runtime.GOOS; os {
case "darwin":
    fmt.Println("OS X")
case "linux":
    fmt.Println("Linux")
default:
    fmt.Printf(os)
}

没有条件的 switch 同 `switch true` 一样
这一构造使得可以用更清晰的形式来编写长的 if-then-else 链

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

defer

延迟函数的执行直到上层函数返回

func main() {
    defer fmt.Println("world")  // 这里相当于在 main 函数 return 之前会被自动调用
    fmt.Println("Hello")
}
 
func main2() {
    // defer 栈, 延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用
    for i := 0; i < 10; i++ {
        defer fmt.Println(i)
    }
    fmt.Println("Hello")
}

指针

Go 的指针用法与 C 一致,但是去除了指针运算

结构体 struct

type Vertex struct {
    X int
    Y int
}
 
v := Vertex{1, 2} // 结构体的初始化
v.X  // 访问结构体中的字段
 
v := Vertex{X: 1} // 可以指定字段初始化
v := Vertex{}  // 也可以不指定任何值初始化
 
// 在结构体中增加成员方法
func (this *Vertex) Print() {
    println(this.X, this.Y)
}

数组

类型 [n]T 是一个有 n 个类型为 T 的值的数组

var a [2]string // 数组的长度是其类型的一部分,因此数组不能改变大小
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
 
var a [2]string{"Hello", "World"} // 或者这样初始化数组
 
a[1:] // 数组支持 slice 切片,用法和 Python 一致
 
// 动态数组
var a []int
append(a, 1) // 使用 append 函数追加数据
 
for i, v := range a {} // 数组可用 range 关键字迭代循环
for i := range a {} // 只需要索引值
for _, v := range a {} // 只需要值

map

map 在使用之前必须用 make 而不是 new 来创建

a := make(map[string]string)
a["Hello"] = "World"
println(a["Hello"])
 
delete(a, "Hello") // 删除元素
 
v, ok = a["Hello"] // 通过双赋值检测某个键存在
 
type Vertex struct {
    Lat, Long float64
}
 
var m = map[string]Vertex{
    "Bell Labs": Vertex{40.68433, -74.39967},
    "Google":    {37.42202, -122.08408}, // 如果顶级的类型只有类型名的话,可以在文法的元素中省略键名
}

interface

接口类型是由一组方法定义的集合

type Abser interface {
    Abs() float64
}
 
type MyFloat float64
 
func (f MyFloat) Abs() float64 { // 方法名称 Abs 满足 Abser 中的定义
    return 0
}
 
var a Abser = MyFloat(100)

并发

goroutine 是由 Go 运行时环境管理的轻量级线程,使用关键词 go func() 即可使用

使用 chan 通道来等待任务执行完成,和获取结果

func sum(c chan int, a int, b int) {
    v := a + b
    c <- v // 将 v 值发送到通道中,发送时候会阻塞所有使用 c 通道的地方
}
 
func main() {
    c := make(chan int) // 无缓冲通道演示, 没有定义大小就是无缓冲通道(只有1个通道),同一时间只能有一个地方调用通道 <- 发送
    c := make(chan int, 2) // 缓冲通道, 开启了一个容量为 2 的通道池,直到池子满了才会发生阻塞
    go sum(c, 400, 200)
    go sum(c, 100, 300)
    sum1, sum2 := <-c, <-c  // 取得结果, 每次调用 <-c 会取得一次结果,没有结果会一直等待, 在取得结果的时候也会阻塞所有使用 c 通道的地方
    println(sum1, sum2)
 
    // 下面只是关闭通道的例子
    close(c) // 可以关闭通道,主要作用于 for i := range c 无限循环取结果的情况
    v, ok := <-c // 非 for range 调用通道可以用双赋值检查是否已经关闭
}
go/learn.txt · 最后更改: 2017/02/07 01:49 由 yefei