在 Clojure 中,数组是一种集合,就像其他语言里的数组一样。和 Lisp 家族的其他语言类似,Clojure 用圆括号来表示数组。
创建 Clojure 数组有两种方式。可以用 list 函数创建数组,也可以用 quote 引用一个字面量数组。
数组的特殊之处在于,Clojure 会把它们当作_调用_来处理。调用必须以一个_运算符_开头,通常是一个函数。数组中剩下的元素则被视为_操作数_,也就是说,它们会成为函数的实参。
Clojure 对数组的这种特殊处理,正是我们不能直接创建数组字面量的原因。用 quote 或其简写 ' 引用一个数组,表示这个数组不应被求值。
与一些现代语言不同,Clojure 的数组是_异构的_,也就是说,它们内部可以包含多种类型的元素,例如 '(2 "a" "b" 3)。
与其他 Lisp 不同,Clojure 中的空数组是真值,并不等同于 nil 或 false。
Elyse 非常期待在即将到来的拉斯维加斯之旅中玩几局扑克(以及其他纸牌游戏)。她是个不折不扣的“自我追踪”爱好者,想写几个小函数来帮自己完成各种记录任务,并请你帮她一起理清思路。
Elyse 特别喜欢扑克,她想记录自己玩了多少轮,以及_具体是哪几轮_。每一轮都有自己的编号,每张牌桌都会显示当前正在进行的轮次编号。Elyse 选了一张牌桌坐下,开始玩第一轮。她打算玩三轮。
实现一个函数 rounds,它接收当前轮次编号,并返回一个 list,其中包含该轮以及接下来要玩的两轮:
(rounds 27)
;;=> (27 28 29)
Elyse 在第一张牌桌玩了几轮,然后休息了一下,又在第二张牌桌玩了几轮……结果每张牌桌都得到了一份不同的列表!她想把这两份列表合并起来,这样就能在同一处记录所有的扑克轮次。
实现一个函数 concat-rounds,它接收两个列表,并返回一个 list,其中先包含第一个 list 中的所有轮次,再包含第二个 list 中的所有轮次:
(concat-rounds '(27 28 29) '(35 36))
;;=> (27 28 29 35 36)
在聊到之前的一些扑克轮次时,另一位玩家说其中两轮玩得格外相似。Elyse 不确定自己有没有玩过那几轮。
实现一个函数 contains-round?,它接收两个参数:一个已玩轮次的列表和一个轮次编号。如果该轮次在已玩轮次的列表中,函数返回 true,否则返回 false:
(contains-round? '(27 28 29 35 36) 29)
;;=> true
(contains-round? '(27 28 29 35 36) 30)
;;=> false
Elyse 想试试一种叫 Black Joe 的新游戏。它和 21 点类似,都是让手里的牌加起来等于一个目标值。不过在 Black Joe 里,目标是让牌面值的_平均值_等于 7。平均值可以先把所有牌面值相加,再用这个和除以手中牌的数量求得。
实现一个函数 card-average,它返回一手 Black Joe 牌的牌面平均值。
(card-average '(5 6 7))
;;=> 6.0
在 Black Joe 里,速度很重要。Elyse 想试着找到一种更快求出平均值的方法。
她想到了两种得到_近似平均值_的方法:
实现函数 approx-average?,它接收 hand,一个包含手中牌面值的列表。
如果上面两种策略中有_一个_or_两个_得到的结果与_实际平均值_相等,就返回 true。
注意:为了更容易找到中位数,所有牌的数量都是奇数。
(approx-average? '(1 2 3))
;;=> true
(approx-average? '(2 3 4 8 8))
;;=> true
(approx-average? '(1 2 3 5 9))
;;=> false
Elyse 对平均值实验的结果很感兴趣,她想知道取_偶数_位置牌面值的平均值,与取_奇数_位置牌面值的平均值,是否会得到相同的结果。该再写一个测试函数了!
实现一个函数 average-even-odd?,它返回一个布尔值,表示偶数下标牌面值的平均值是否与奇数下标牌面值的平均值相同。
(average-even-odd? '(1 2 3))
;;=> true
(average-even-odd? '(1 2 3 4))
;;=> false
在 Black Joe 里,每第 11 手牌都是奖励牌,附带一条奖励规则:如果你抽到的最后一张牌是 Jack,就把它的值加倍。
实现一个函数 maybe-double-last,它接收一手牌,并检查最后一张牌是不是 Jack(11)。如果最后一张牌是 Jack(11),就先把它的值加倍,再返回这手牌。
(maybe-double-last '(5 9 11))
;;=> '(5 9 22)
(maybe-double-last '(5 9 10))
;;=> '(5 9 10)