Please enable Javascript to view the contents

Go pics

 ·  ☕ 8 分钟  ·  ✍️ Jacklanda

Go pics 即:Go pieces,意为:Go片段,

当我们忘记了某个语法细节的时候,
这时候最简单的解决办法不是google,而是使用
一份自己熟悉的速查表,以下希望建立一个
Go语言的速查表,其中的内容,
主要是自己在学习过程中遇到的一些常见的segments
关键字&预定义名字
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
break         default         func        interface    select
case          defer           go          map          struct
chan          else            goto        package      switch
const         fallthrough     if          range        type
continue      for             import      return       var

/* 内部预定义名字,你可以重新定义它们 */
内建常量:    true       false      iota      nil
内建类型:    int        int8       int16     int32     int64
             uint       uint8      uint16    uint32    uint64    uintptr
内建函数:    make       len        cap       new       append    copy    close    delete
             complex    real       imag      panic     recover
命名规则
1
2
3
4
5
6
Go的语言风格是尽量使用短小的名字对于局部变量更是如此
通常来讲如果一个名字的作用域较大那么它的生命周期也会比较长
此时使用较长的名字将更有意义

习惯上Go 使用驼峰式命名区别于Python PEP8 推荐的的蛇形命名),
即优先使用大小写分隔避免使用下划线分隔
常用的基本数据类型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* 整型 */
----------|------------------------------------
type      | intr
----------|------------------------------------
(u)int8   | /有符号的8位整型长度为 8bit
(u)int16  | /有符号的16位整型长度为 16bit
(u)int32  | /有符号的32位整型长度为 32bit
(u)int64  | /有符号的64位整型长度为 64bit
(u)int    | 根据编译平台表示32位/64位的无/有符号的整型常用!
----------|------------------------------------

byte等同于int8 代表一个8位的整型适用于单个ascii字符
rune等同于int32代表一个32位的整型适用于单个utf-8字符

字符串类型string底层是通过byte数组实现的

符合类型数组类型切片类型结构体类型
Map类型指针类型函数类型接口类型Channel类型

常量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* 常量中的数据类型只可以是数字型、布尔型以及字符串型 */
const Id int = 10
const isBoy bool = false
const Gender string = "female"

/* 枚举常量 */
const (
	Id = 10
    Limit = false
    Gender = "female"
)
变量声明
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Go 的变量名只能由字母或下划线开头整体由一个或多个字母数字下划线组成
-----------------
var _str string
_str1 = "Jerry ~ I'll have u for dinner!"
-----------------
var _str2 string = "<RUNNING!!!>"
var _str3 = "GO BACK TO HOME!!!"    //如果声明的同时未进行赋值,那么变量可声明也可不声明其类型
-----------------
_str4 := "Tom still got nothing today"	//简短声明,编译器将根据赋值类型自判变量类型「简短声明不能用来定义全局变量!!!」
-----------------
var id, name, isBoy = 1, jacklanda, true
-----------------
var (
    isVip bool
    date string
    member int
)
p.s. 赋值后的变量需要使用如果不使用编译将报错 *a declared and not used*
分支结构
 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
30
31
32
33
34
35
<1>:/* 需要注意的是:
       Go编译器强制if-else if-else结构中的
       左花括号「{」必须和关键字位于同一行;
       右花括号「}」必须与接下来的关键字位于同一行
       否则将编译不过 */
if 条件1 {
    ...
} else if 条件2 {
    ...
} else {
    ...
}

<2>:
if 赋值语句; 条件1 {
    ...
} else if 条件2 {
    ...
} else {
    ...
}

<3>:/* 开关语句 */
var name string
fmt.Print("请输入你的姓名:")
fmt.Scanln(&name)
switch {
    case name == "Jacklanda":
        fmt.Print("你是", name)
    case name == "Michael":
        fmt.Print("你是", name)
    default:
        fmt.Print("请输入你的姓名哟~")
}

for 循环
1
2
3
4
/* for循环:「for」是Go中唯一可用的循环标识符:*/
for i := 0; i < 10; i++ {
    fmt.Printf("%d", i);
}
指针
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 和C一样,Go中的指针操作符有两个:
   「&」--> 取地址符;
   「*」--> 解引用符。
*/

/* 方法一:先定义普通变量,再对普通变量取地址来定义指针变量 */
num := 1
ptr := &num
/* 同样,这里换成显式声明的形式则有 */
num := 1
var ptrInt *int
ptrInt = &num

/* 方法二:先创建指针,再给指针指向的内存地址写入值 */
str := new(string)
*str = "我是写入的值"

/* 打印指针指向的内存地址方法 */
//第一种:
fmt.Printf("%p", ptr)

//第二种:
fmt.Println(ptr)
array 数组
 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
30
31
/* C里我常用的顺序存储的容器类型有静态数组和动态数组 */
/* Python里则常用list对象和deque双端队列,*/
//在 Go 中,Go给我们提供了两个最基本的容器类型:array数组&slice切片

/* 用法示例:*/
<1> var array [10]int //声明一个长度为10的整数数组,遍历赋值
    for i := 0; i<10; i++ {
        array[i] = i;
    }
<2> array := [10]int{0, 1, 2, ... , 9}  //声明+元素赋值
    array := [3]int{0}  //可以不必为所有元素赋值
    array := [...]int{0, 1, 2}  //一种「懒惰」的写法,让编译器去识别数组中有几个元素

<3> var array_1 [10]int
    var array_2 [5]int  //array_1 与 array_2 非同类型,因为数组大小亦为数组类型的一部分

<4> /* Go数组为值类型,而非引用类型,类似Python中对list对象的深拷贝,
    同样,数组作为一个参数传入函数时,相当于创建了一个副本,副本
    内的值发生变化,将不会影响原数组; */

<5> len(array)  //len()函数返回数组的长度

<6> /* Go 中也有同时返回索引和索引值的数组遍历方式 */
func main() {
    array := [3]int{11, 22, 33}
             for index, value := range array{  //亦可用空白符「_」来缺省index索引值
                fmt.Printf("第%d个元素的值为:%d\n", index, value)
             }
}

<7> /* 多维数组:略 */
Go 切片
 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* Go数组一旦声明,之后将不可变长,即:immutable,
   在这点上,要区别于Python中list对象的split()切片方法,
   但也可以类比Python中的不可变容器对象tuple,
   为解决这个痛点,我们应当考虑多用-->slice切片 */

<1> s_1 := [3]int{1, 2, 3};  //简单声明一个切片
    var s_2 []int = s_1[1:2]  //将已有切片部分赋给新声明的切片
    s_3 := []int{111, 222, 333}  //用创建一个数组的形式,然后再赋给一个slice

/* 我的理解是,切片是数组的封装,在数组的基础上增加了修改的功能
   多个切片可共享同一个数组,每个切片所做出的更改都将反映在数组中 */

/* 区别切片的len()和cap()方法:len()方法返回切片的元素数,
   cap()方法返回从切片建立的始端到数组末端的长度,即:切片的容量
   此二者不等价! */
<2> person_array := [...]string{"Jacklanda", "Michael", "Yon", "洋洋"}
    person_slice := person_array[0:2]
    length := len(person_slice)  //输出:3
    capacity := cap(person_slice)  //输出:4

<3> slice_by_make := make([]int, 3, 3)  //使用make创建切片,切片值默认初始化为0

/* 同样,我还是习惯拿自己熟悉的事物进行类比,
   Go的切片的是对数组的封装,这样人不免想起:在CPython中,list对象是由栈帧对象
   内维护的一个动态数组来实现的,假设这么一个情景:对一个list对象不断地append(),
   可是list对象好像是个无底洞,你扔什么东西进去,它一概全收。在这里,list对象的
   自动扩容机制起了主导作用:解释器发现list对象现有空间不够用-->创建一个保留有一
   定裕量的新的C动态数组,然后将原动态数组存储的所有指针拷贝到新的动态数组中。
   -->完成超容量情况下的list对象追加方法的实现
   同样,Go的切片的不断append也是基于类似的思想:每当有新元素追加到切片时,Go编
   译器将会创建一个新数组,将现有数组的元素复制到这个新数组中,并返回这个新数组
   的新切片引用,从而实现Go切片的扩容 */

<4> persons := []string{"David Beazley", "Ken Thompson", "Danis Ritchie"}
    persons := append(persons, "Van Guido Rossum")
    /* 在上面的这个例子中,向切片中追加新的元素时,编译器发现原切片的容量不够用了,
       此时便会调用位于go/src/runtime/slice.go下的growslice()函数:
func growslice(et *_type, old slice, cap int) slice {
    if raceenabled {
        callerpc := getcallerpc()
        racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice))
    }
    if msanenabled {
        msanread(old.array, uintptr(old.len*int(et.size)))
    }

    if cap < old.cap {
        panic(errorString("growslice: cap out of range"))
    }

    if et.size == 0 {
        // append should not create a slice with nil pointer but non-zero len.
        // We assume that append doesn't need to preserve old.array in this case.
        return slice{unsafe.Pointer(&zerobase), old.len, cap}
    }

    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        if old.len < 1024 {
            newcap = doublecap
        } else {
            // Check 0 < newcap to detect overflow
            // and prevent a
    通过该函数来实现slice的扩容 */

<5> dish := []string{"手撕鸡", "玫瑰豉油鸡", "白切鸡"}
    drink := []string{"北冰洋", "冰峰", "健力宝", "美年达"}
    diet := append(dish, drink...)  //「...」运算符表示可变参数


Goroutine 并发
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 对于Go服务器,每接收到一个请求的时候,都会创建一个新的Conn,
  这个Conn里保存了这次请求的信息,然后这些信息再被传递到对应的
  处理函数(handler)中,该处理函数就可以读到这个请求的header信息,
  如此便保证了高并发下每个请求的独立性。*/

/* 信道<-channel */

//声明方式一
var tube chan string
tube = make(chan string)

//简短声明
tube := make(chan string)

//向信道发送数据
tube <- "天王盖地虎"
tube <- "宝塔镇河妖"

//从信道读取数据,并赋给变量
data := <- tube

//信道使用完毕后,应及时关闭
//若已关闭,则返回赋给变量ok的值为false,否则为true
data, status := <- tube
字符串方法
1
2
/* 比较两个字符串 */
<1> fmt.Print(strings.Compare(str1, str2))
面向对象
 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
30
/* 区别于Python、Java等使用「定义类」的方式,Go实现面向对象编程范式的方法比较独特 */

/* 在Python中,我们通常会这么写:*/
class objOriented(object):

    def __init__(self):
        self.say_hello()

    def say_hello(self):
        name = input("请输入你的名字:")
        print(f"{name} ,你好呀~")
        return

objOriented()

/* 而Golang 的实现方式是:通过给任意的类型定义方法来实现面向对象编程这一概念
   举个栗子,下面这段代码通过给Obj这个自定义类型定义了方法sayHello()
*/
    type Obj string

    func (obj *Obj) sayHello(){
        fmt.Println(obj, "你好呀~")
    }

    func main(){
        var obj Obj
        fmt.Println("请输入你的名字:")
        fmt.Scanln(&obj)
        obj.sayHello()
    }