张洋铭Ocean

30分钟搞定GO语言(三)

2017-01-28  本文已影响314人  张洋铭Ocean



基础

一开始,将学习关于语言的所有基础内容。学习如何基于已有类型定义新的类型:盖了结构体、数组、slice 和 map。

指针

Go 具有指针。 指针保存了变量的内存地址。

类型 *T 是指向类型 T 的值的指针。其零值是 nil 。

var p *int

& 符号会生成一个指向其作用对象的指针。

i := 42
p = &i

* 符号表示指针指向的底层的值。

fmt.Println(*p) // 通过指针 p 读取 i
*p = 21         // 通过指针 p 设置 i

这也就是通常所说的“间接引用”或“非直接引用”。

与 C 不同,Go 没有指针运算。

结构体

一个结构体( struct )就是一个字段的集合。

(而 type 的含义跟其字面意思相符。)

结构体字段

结构体字段使用点号来访问。

结构体指针

结构体字段可以通过结构体指针来访问。

通过指针间接的访问是透明的。

结构体文法

结构体文法表示通过结构体字段的值作为列表来新分配一个结构体。

使用 Name: 语法可以仅列出部分字段。(字段名的顺序无关。)

特殊的前缀 & 返回一个指向结构体的指针。

数组

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

表达式

var a [10]int

定义变量 a 是一个有十个整数的数组。

数组的长度是其类型的一部分,因此数组不能改变大小。 这看起来是一个制约,但是请不要担心; Go 提供了更加便利的方式来使用数组。


Slice

数组具有固定大小。另一方面,slice是对数组元素的特殊处理,动态大小,数组元素视图选取灵活。在实践中,切片比数组更常见。

类型[]T是类型的元素的slice T。本质上就是不指定大小。

这个表达式创建数组的第一个五行片a:

a [0:5]


Slice类似于数组的引用

slice不存储任何数据,它只是描述了底层数组的一部分。

更改切片的元素会修改其底层数组的相应元素。共享相同底层数组的其他slice将看到这些更改。

Slice文本

slice文本相当于数组文本去掉了指定长度。

下面是一个数组文本:

[3]bool{true, true, false}

下面是一个slice文本,这个slice文本先内部创建了一个如上的数组,然后创建了一个slice结构指向了内部的数组:

[]bool{true, true, false}


Slice 边界默认值

当选择slice的范围时,你可以忽略声明低边界或者高边界。低边界的默认值是0,高边界的默认值是数组的长度。

对下面的数组来说:

var a [10]int

以下的slice表达式是相等的:

a[0:10]
a[:10]
a[0:]
a[:]

Slice的长度和容量

slice既有长度又有容量。

slice的长度是它包含的元素数。slice的容量是底层数组中元素的数量,从slice中的第一个元素计数。

slice的长度和容量可以使用表达式得到len(s)和cap(s)。

只要有足够的容量,就可以通过重新切片来延长slice的长度。如果slice调整后的长度,超出底层的数组的长度,则会报错:

panic: runtime error: slice bounds out of range


Nil Slice

slice 的零值是 nil 。

一个 nil 的 slice 的长度和容量是 0,并且没有底层相关联的array。


构造 slice

slice 由函数 make 创建,通过这个内置函数可以创建动态长度的数组。

这会分配一个全是零值的数组并且返回一个 slice 指向这个数组:

a := make([]int, 5)  // len(a)=5

为了指定容量,可传递第三个参数到 make:

b := make([]int, 0, 5) // len(b)=0, cap(b)=5

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4


Slices of slices

Slice能够包含任何类型,包括其他的slice.


向 slice 添加元素

向 slice 的末尾添加元素是一种常见的操作,因此 Go 提供了一个内建函数 append 。 内建函数的文档对 append 有详细介绍。

func append(s []T, vs ...T) []T

append 的第一个参数 s 是一个元素类型为 T 的 slice ,其余类型为 T 的值将会附加到该 slice 的末尾。

append 的结果是一个包含原 slice 所有元素加上新添加的元素的 slice。

如果 s 的底层数组太小,而不能容纳所有值时,会分配一个更大的数组。 返回的 slice 会指向这个新分配的数组。

(了解更多关于 slice 的内容,参阅文章https://blog.go-zh.org/go-slices-usage-and-internals)


Range

for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。

当使用 for 循环遍历一个 slice 时,每次迭代 range 将返回两个值。 第一个是当前下标(序号),第二个是该下标所对应元素的一个拷贝。

可以通过赋值给 _ 来忽略索引或值。

如果只需要索引值,去掉 “ , value ” 的部分即可。

做一个练习,这个练习的代码我结合了指针一起。实现 Pic 。它返回一个长度为 dy 的 slice,其中每个元素是一个长度为 dx 且元素类型为8位无符号整数的 slice。当你运行这个程序时, 它会将每个整数作为对应像素的灰度值(好吧,其实是蓝度)并显示这个 slice 所对应的图像。

计算每个像素的灰度值的方法由你决定;几个有意思的选择包括 (x+y)/2、x*y 和 x^y 。

(需要使用循环来分配 [][]uint8 中的每个 []uint8 。)

(使用 uint8(intValue) 来在类型之间进行转换。)


Map

Map 映射键到值。Map的零值是nil. 一个nil的map没有键,也不能添加键。 map 在使用之前须用 make 来创建,会自动完成初始化;


Map 的文本

map 的文本跟结构体文本相似,不过必须有键名。

若顶级类型只是一个类型名,你可以在文法的元素中省略它。

修改 Map

在 map m 中插入或修改一个元素:

m[key] = elem

获得元素:

elem = m[key]

删除元素:

delete(m, key)

通过双赋值检测某个键存在:

elem, ok = m[key]

如果 key 在 m 中, ok 为 true。否则, ok 为 false,并且 elem 是 map 的元素类型的零值。

同样的,当从 map 中读取某个不存在的键时,结果是 map 的元素类型的零值。

如果elem和ok之前没有被声明,可以直接使用自动类型推导

elem, ok := m[key]

做一个练习,实现 WordCount。它应当返回一个含有 s 中每个 “词” 个数的 map。函数 wc.Test 针对这个函数执行一个测试用例,并输出成功还是失败。


函数值

函数也是值。他们可以像其他值一样传递,同样,函数值可以作为函数的参数或者返回值。


函数的闭包

Go 函数可以是一个闭包。闭包是一个函数值,它引用了函数体之外的变量。 这个函数可以对这个引用的变量进行访问和赋值;换句话说这个函数被“绑定”在这个变量上。

例如,函数 adder 返回一个闭包。每个返回的闭包都被绑定到其各自的 sum 变量上。

现在来通过函数做些有趣的事情。

实现一个 fibonacci 函数,返回一个函数(一个闭包)可以返回连续的斐波纳契数。

◆  ◆  ◆  ◆  ◆  

来源:

作者介绍:张洋铭,投资人中最懂动漫的程序员,负责PlugandPlay早期科技类项目投资,个人关注动漫智能助理。

微信公众号:张洋铭Ocean(ocean_anidata)

BP请投递至:ocean.zhang@plugandplaychina.com

上一篇 下一篇

猜你喜欢

热点阅读