『The Go Programming Language』四、复合数据类型

复合数据类型由基础数据类型组合构成。
需要明确:数组和结构体都是聚合类型,值由内存中一组变量组成;

4.1数组

长度固定拥有0或多个相同数据类型的元素序列。由内置函数len()返回长度。

var a [3]int
fmt.Println(a[0])
fmt.Println(a[len(a)-1])    // 即 a[2]

数组的初始值是一组零值;也可以使用数组字面量来初始化。

var q [3]int = [3]int{1, 2, 3}
f := [...]int{1, 2, 3}

fq都是长度为3的数组;后者[...]表示长度由初始化数组元素个数确定。不过直接用[]不也能行吗?怎么这里没强调。懂了,这样直接变成slice了。

数组的长度必须是常量表达式,即长度必须在编译时确定
数组的长度是数组的一部分,这也表明[3]int[4]int是两种类型。

q := [4]int{1, 2, 3, 4}
q = [3]int{1, 2, 3}    // cannot use [3]int{…} (value of type [3]int) as [4]int value in assignment

说到这就不得不提一下C/C++:本来他俩也是必须固定长度;但是因为一些编译器的支持,像是:

int n;
std::cin>>n;
int a[n];

这种骚操作也能用;不过不对就是了。

symb := [...]string{7: "it"}中给出的7是引索,不过引索可以乱序给出。没有被指定的元素为零值。本例中len(symb)为8.

数组可比较(假设元素类型可以比较),比较两侧的值是否相同。
上一章中说到类型相同才可比较,如[3]int和[4]int根本无法比较,因为编译错误。

需要注意:调用函数并传入参数时会创建一个副本然后复制给对应变量。也就是说不像Java那样存在隐式引用传递。

func test(ptr *[3]int) {
	for _, i := range ptr {
		println(i)
	}
}

这就是一个指针典例。需要注意:range关键字产生引索和引索对应的值,而不是类型的引索和第引索个值

4.2 slice

slice是长度可变的相同元素集。常为[]T形式。
可以通过[]int{1,2,3}初始化,这样同时创建底层数组和指向它的slice。

slice存在底层数组,拥有指针、长度、容量三个属性。这导致slice可以随意切片。同时一个底层数组可以对应多个slice。
其中指针指向第一个可以从slice访问的元素(不是底层数组的),长度是slice元素个数,容量是slice的起始元素到底层数组末尾。
同时因为包含指针,所以可以函数内更改内容。
依靠len()cap()返回长度和容量。
依靠s[i:j]的形式创建slice,“引用”i到j-1个。

引用slice可以超过被引用slice的长度,但是不能超过容量。前者只会增长,后者会宕机。

对string子串和[]byte的slice格式相同,同时在底层上他们也相同。
但是区别在于返回结果分别是字符串和字节slice。这是本质上的区别。

不同于数组,slice不能用==比较(除了[]byte有标准库),要自己实现比较(但是都可以和nil比较)。
slice的零值是nil,这时slice没有底层数组。如果想查询slice是否为空就用len() == 0而不是== nil

make([]T, len, cap)创建一个cap容量长的无名数组并slice引用0~len+1。

4.2.1 append函数

func append(slice []Type, elems ...Type) []Type为slice追加。
通过slice的结构和append()move()等函数可以实现多种数据结构。
比如栈stack等。

4.3 map

map是散列表的引用,表示为map[Key]Value,其中Key必须是可比较类型。用map[string]int{}make(map[string]int)创建。
可以使用func delete(m map[Type]Type1, key Type)删除一个key。
map的元素顺序不固定,你可以认为每一次range都是随机顺序。(想按照特定顺序遍历可以单独存储一个key序列)

map的元素不是一个变量,不能用&获取地址。
原因之一是map的元素可能会被散列到其他位置,这时地址无效。

通过下标方式访问map一定会有值:

  • key在map:得到key对应值。
  • key不在map:得到map类型的零值。

可以通过if item, ok := items["isit?"]; !ok { /* "isit?"不是键,此时ok为fales */ }区分元素不存在和存在但为0的情况。
map的零值是nil,这时没有引用任何散列表;向nil的map里存储数据会报错。

和slice一样,map不能比较。 只能与nil比较。
这导致你要是过程中可能传递某个slice,因为它不能比较,这就要另辟蹊径。
《The Go Programming Language》给出的是做一个slice->string的func。

4.4 结构体

type Srut struct {
	Key  string // 可导出
	info string // 不可导出
}

显然,struct成员遵守首字母可见性原则。
如果一个variable是一个struct本身,可以使用.访问,如果一个指针指向struct,可以先解引用让然后访问。

插一句题外话,如果比较go/c/c++,他们对于指针指向struct/object这件事有不同

  • go:指针解引用然后访问。
  • c/c++:可以解引用( *指针 ) . 成员,也可以直接指针 -> 成员访问。

上例中 type 是起一个别名(具体参照变量那一章),所以Srut和struct{ … }是一个意思。
也就是说struct{}是一个空结构的类型名,那么struct{}{}就是空结构初始化。

4.4.1 && 4.4.2 结构体字面量和比较

结构体值可以使用结构体字面量设置。

g1 := gif.GIF{LoopCount: 10} // 可以指定初始化
s1 := srut{"isam", "are?"}   // 也可以不指定,但是会严格按照顺序初始化。

如果结构体所有成员都可以比较,那么这个结构体可以比较。
其中==就是按照顺序比较。

4.4.3 结构体嵌套与匿名

结构体中可以包含结构体,但是这样会存在多级.访问的情况。
我们可以在结构体中包含匿名结构体,这样可以一个.访问。

type Point struct {
	x, y float64
}

type Circle struct {
	Point
	Radius float64
}

S1 := Circle{Point{1.0, 2.0}, 3.0}
S1.x = 1.0

不过很遗憾,上面的初始化变量是唯一一种方式,毕竟也是嵌套结构不是。

4.5 JSON

JSON包含数字、布尔、字符串。用""引的是Unicode码点序列,使用\转义。都写成name:vale格式。
只有可导出成员可以转为json。
go圣经

Year  int  `json:"released"`
Color bool `json:"color,omitempty"`

结构体的成员Tag可以是任意的字符串面值,但是通常是一系列用空格分隔的key:"value"键值对序列;
因为值中含有双引号字符,因此成员Tag一般用原生字符串面值的形式书写。
json开头键名对应的值用于控制encoding/json包的编码和解码的行为,并且encoding/…下面其它的包也遵循这个约定。
成员Tag中json对应值的第一部分用于指定JSON对象的名字,比如将Go语言中的TotalCount成员对应到JSON中的total_count对象。
Color成员的Tag还带了一个额外的omitempty选项,表示当Go语言结构体成员为空或零值时不生成该JSON对象(这里false为零值)。

json包中的marshal()unmarshal()进行序列化和反序列化。
反序列化如果过长,超出部分丢弃。不过在反序列化过成中:json字段到go成员是不需要注意大小写的(但是结构体必须全大写);因此需要Go语言结构体成员Tag来指定对应的JSON名字。

4.6 文本和HTML模板

非常抽象内容,使我无法学习。

有时需要格式和代码分离出来以便更安全地修改。
这些功能是由 text/template 和 html/template 等模板包提供的,它们提供了一个将变量值填充到一个文本或HTML格式的模板的机制。

一个模板是一个字符串或一个文件,里面包含了一个或多个由双花括号包含的{{...}}对象,这称为操作。
一个操作可以:打印值、选择结构体的成员、调用函数或方法、表达式控制流(if-elserange)、实例化模板等。

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值