Clojureでは、リストはコレクションです。他の言語のリストと同じようなものです。Lispファミリーの言語と同じように、Clojureはリストを表すのに括弧(())を使います。
Clojureのリストは、2つの方法のいずれかで作成できます。list関数でリストを作るか、リストリテラルをquoteするかです。
リストが特別なのは、Clojureがリストを_呼び出し_として扱うからです。呼び出しは_演算子_で始まると想定されており、ふつうはこれが関数になります。リストの残りの要素は_被演算子_とみなされ、関数の引数になります。
Clojureがリストを特別に扱うため、リストリテラルを直接作ることはできません。quoteまたはその省略形'を使ってリストを引用すると、そのリストを評価しないよう指示したことになります。
一部のモダンな言語とは異なり、Clojureのリストは_異種混合_です。つまり、内部に複数の型の要素を含めることができます。たとえば'(2 "a" "b" 3)のようになります。
他のLispとは異なり、Clojureでは空のリストは真とみなされ、nilやfalseと同じではありません。
Elyseは、もうすぐ始まるラスベガス旅行でポーカー(やその他のカードゲーム)をプレイするのをとても楽しみにしています。大の「セルフトラッキング」好きである彼女は、記録作業に役立つ小さな関数をいくつか作りたいと考えており、その内容を一緒に考えてほしいと頼んできました。
Elyseはとりわけポーカーが好きで、自分が何ラウンドプレイしたのか、そしてそれが_どのラウンド_なのかを記録したいと思っています。各ラウンドにはそれぞれ番号があり、どのテーブルにも現在プレイ中のラウンド番号が表示されています。Elyseはテーブルを1つ選んで席に着き、最初のラウンドをプレイします。彼女は3ラウンドプレイする予定です。
現在のラウンド番号を受け取り、そのラウンドと、このあと続く_次の2つ_のラウンドを含む1つのlistを返すroundsという関数を実装しましょう。
(rounds 27)
;;=> (27 28 29)
Elyseは最初のテーブルで何ラウンドかプレイし、それから休憩を挟んで2つ目のテーブルでも何ラウンドかプレイしました。ところが、テーブルごとに別々のリストになってしまいました。彼女は2つのリストを1つにまとめて、すべてのポーカーのラウンドを同じ場所で記録したいと考えています。
2つのリストを受け取り、1つ目のlistにあるすべてのラウンドのあとに2つ目のlistのすべてのラウンドを続けた、1つのlistを返すconcat-roundsという関数を実装しましょう。
(concat-rounds '(27 28 29) '(35 36))
;;=> (27 28 29 35 36)
以前のポーカーのラウンドのいくつかについて話していると、別のプレイヤーが、そのうち2つはとても似た展開だったと口にしました。Elyseは、そのラウンドを自分がプレイしたのかどうか、よく分かりません。
2つの引数、プレイしたラウンドのリストとラウンド番号を受け取る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という新しいゲームを試してみたくなりました。これはBlack Jackによく似ています。Black Jackでは、手札のカードの合計を目標の値にすることが目的ですが、Black Joeでは、カードの値の_平均_を7にすることが目的です。平均は、すべてのカードの値を合計し、その合計を手札の枚数で割ることで求められます。
Black Joeの手札の平均値を返すcard-averageという関数を実装しましょう。
(card-average '(5 6 7))
;;=> 6.0
Black Joeではスピードが重要です。Elyseは、平均をより速く求める方法を探してみることにしました。
彼女は、_平均に近い_数を求める方法を2つ思いつきました。
手札のカードの値を並べたリストであるhandを引数に取る、approx-average?という関数を実装しましょう。
上に挙げた方法の_どちらか_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は、_偶数_の位置にあるカードの平均と、_奇数_の位置にあるカードの平均が同じ結果になるかどうか気になっています。テスト用の関数をもう1つ作ってみましょう!
偶数インデックスのカードの平均が、奇数インデックスのカードの平均と同じかどうかを示す真偽値を返すaverage-even-odd?という関数を実装しましょう。
(average-even-odd? '(1 2 3))
;;=> true
(average-even-odd? '(1 2 3 4))
;;=> false
Black Joeでは、11回目の手札はボーナスルール付きのボーナスハンドになります。最後に引いたカードがジャックなら、その値を2倍にします。
手札を受け取り、最後のカードがジャック(11)かどうかを確認するmaybe-double-lastという関数を実装しましょう。最後のカードがジャック(11)である場合は、手札を返す前にその値を2倍にします。
(maybe-double-last '(5 9 11))
;;=> '(5 9 22)
(maybe-double-last '(5 9 10))
;;=> '(5 9 10)