Map
Map就是Key-value对。map必须make
后才能使用,nil
的map
是无法赋值的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package main import "fmt" type Vertex struct { Lat, Long float64 } var m map[string]Vertex func main() { m = make(map[string]Vertex) m["Bell Labs"] = Vertex{ 40.68433, -74.39967, } fmt.Println(m["Bell Labs"]) }
|
map literals
map literals
和struct literals
是类似的,不过key
是必须的。
1 2 3 4 5 6 7 8 9 10 11 12
| type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": Vertex{ 40.68433, -74.39967, }, "Google": Vertex{ 37.42202, -122.08408, }, }
|
如果顶层类型只是一个类型名,可以将它从literal
元素们中省略。
1 2 3 4
| var m = map[string]Vertex{ "Bell Labs": {40.68433, -74.39967}, "Google": {37.42202, -122.08408}, }
|
Map操作
新建或更新map项
取值
删除元素
检测key是否存在
1 2
| elem, ok = m[key] elem, ok := m[key] //短定义
|
如果存在,则返回 值+true,否则返回对应类型的0值和false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main import "fmt" func main() { m := make(map[string]int) m["Answer"] = 42 fmt.Println("The value:", m["Answer"]) m["Answer"] = 48 fmt.Println("The value:", m["Answer"]) delete(m, "Answer") fmt.Println("The value:", m["Answer"]) v, ok := m["Answer"] fmt.Println("The value:", v, "Present?", ok) }
|
Map练习
实现一个WordCount,统计一个string
中每个单词的频度,这个练习可以本地做,也可以在golang.org的交互式网页做,它提供一个结果校验功能。
提示,可以使用strings
包中的Fields
方法
https://tour.golang.org/moretypes/20
以下是我的答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package main import ( "golang.org/x/tour/wc" "strings" ) func WordCount(s string) map[string]int { r := make(map[string]int) for _,w := range strings.Fields(s){ r[w] += 1 } return r } func main() { wc.Test(WordCount) }
|
结果
1 2 3 4 5 6 7 8 9 10 11 12
| PASS f("I am learning Go!") = map[string]int{"I":1, "am":1, "learning":1, "Go!":1} PASS f("The quick brown fox jumped over the lazy dog.") = map[string]int{"fox":1, "jumped":1, "quick":1, "brown":1, "over":1, "the":1, "lazy":1, "dog.":1, "The":1} PASS f("I ate a donut. Then I ate another donut.") = map[string]int{"Then":1, "another":1, "I":2, "ate":2, "a":1, "donut.":2} PASS f("A man a plan a canal panama.") = map[string]int{"A":1, "man":1, "a":2, "plan":1, "canal":1, "panama.":1}
|
可见这种0缺省值的设定还是很好用的,可以让程序简练。
函数value
在Go中函数也是值,可以用作函数参数与返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package main import ( "fmt" "math" ) func compute(fn func(float64, float64) float64) float64 { return fn(3, 4) } func main() { hypot := func(x, y float64) float64 { return math.Sqrt(x*x + y*y) } fmt.Println(hypot(5, 12)) fmt.Println(compute(hypot)) fmt.Println(compute(math.Pow)) }
|
以上述例子中,compute
函数的参数是一个2个参数和返回值都是float64
的函数。变量hypot
的值是一个函数。
函数闭包
Go函数可以是闭包。所谓闭包是一个引用了外部变量的函数值,这个函数可以存取和赋值给变量。这种意义上该函数被绑定到变量。
下面的例子中,adder
函数返回一个闭包。每个闭包被绑定到它自有的sum
变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i < 10; i++ { fmt.Println( pos(i), neg(-2*i),) } }
|
运行:
1 2 3 4 5 6 7 8 9 10
| 0 0 1 -2 3 -6 6 -12 10 -20 15 -30 21 -42 28 -56 36 -72 45 -90
|
闭包练习:Fibonacci数列
题目就不用解释了,菲波那契数列大家都很熟悉,由0,1开始,之后每个元素都是前二者的和。0, 1, 1, 2, 3, 5, …
我的答案(参考之后的结果,一开始是懵逼的,居然不带参数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main import "fmt" func fibonacci() func()int{ x,y:=0,1 return func()(r int) { r=x x,y = y,x+y return } } func main(){ f:=fibonacci() for i:=0;i<10;i++ { fmt.Println(f()) } }
|
运行:
很有趣是不是?首先f:=fibnacci() 定义了function value
,这是闭包的前提。按我的猜测,此时为f
分配了内存空间,包括其中的变量x
、y
,所以在f
的生命周期内,x
,y
是一直存在的,因此当匿名函数一直操作其外部的x
,y
时,其变化一真累积,这也是fibonacci()不用传入参数的原因。最后fibonacci应该从0开始返回,这里也用到了一个Go特有的return
方式来实现。