...

Codewalk: Go中的一等函数

弹出代码
代码位置 左侧右侧 代码宽度 70% 文件路径 显示隐藏
引言
Go支持一等函数、高阶函数、用户定义的函数类型、函数字面、闭包以及多返回值。

此丰富的特性集在强类型语言中支持函数式编程风格。

在这个代码漫步中,我们将看到一个简单的程序,它模拟了一个叫做 Pig 的骰子游戏并求值基本的策略。
doc/codewalk/pig.go
游戏概览
Pig是由两个玩家掷6面骰子的游戏。在每一轮中,你可能掷骰或停留。
  • 如果你掷出了1,就会失去你在这轮中的所有点数,并交由你的对手玩。 掷出的其它值都将计入你这轮的分数中。
  • 如果你停留了,你这轮的分数就会计入你的总分中,并交由你的对手玩。
总点数先达到100的人胜出。

score 类型存储了当前的分数与对手玩家,还有当前这一轮中累积的点数。
doc/codewalk/pig.go:27,30
用户定义函数类型
在Go中,函数可以像其它值那样到处传递。一个函数的类型签名描述了其实参与返回值的类型。

action 是一个函数,它接受一个 score 并返回产生的 score 与当前一轮是否结束。

如果当前一轮结束,则产生的 score 中的 playeropponent 字段就会互换,然后就轮到另一个玩家了。
doc/codewalk/pig.go:34,35
多返回值
Go的函数可以返回多个值。

函数 rollstay 都返回一对值。它们也匹配 action 的类型签名。这些 action 类型的函数定义了Pig的规则。
doc/codewalk/pig.go:41,59
高阶函数
一个函数能够将其它函数作为实参和返回值。

strategy 函数接受一个 score 作为输入并返回一个 action 来执行。
(记住,action 本身就是一个函数。)
doc/codewalk/pig.go:63
函数字面与闭包
Go中可以声明匿名函数,就像这个例子中那样。函数字面是闭包的: 它们继承了声明它们所在函数的作用域。

Pig中的一个基本策略就是继续掷骰直到你在本轮中累计的点数至少为 k,然后停留。 实参 k 包含在此函数字面中,该函数字面与 strategy 类型签名相匹配。
doc/codewalk/pig.go:70,75
模拟游戏
我们这样来模拟一场Pig游戏:通过调用一个 action 来更新 score,直到一个玩家达到100点为止。每一个 action 都通过调用与当前玩家相关联的 strategy 函数来确定。
doc/codewalk/pig.go:80,95
模拟一场比赛
roundRobin 函数模拟一场比赛并记录输赢。每一个策略与其它策略互相比 gamesPerSeries 次。
doc/codewalk/pig.go:99,117
变参函数声明
ratioString 这样的变参函数接受数量可变的实参。 这些参数实参在该函数中可作为一个切片使用。
doc/codewalk/pig.go:123,126
模拟的结果
main 函数定义了100个基本策略,模拟一场比赛,并打印出每一个策略的输/赢记录。

在这些策略中,最好是停留了25次,不过 Pig的最佳策略 则更加复杂。
doc/codewalk/pig.go:142,153