紙牌遊戲

紙牌遊戲

學習練習

簡介

在 Clojure 中,陣列是一種集合,就像其他語言的陣列一樣。和其他 Lisp 家族的語言類似,Clojure 用括號來表示陣列。

Clojure 陣列可以透過兩種方式建立。你可以用list函式建立陣列,也可以對字面值陣列使用quote。

陣列之所以特別,是因為 Clojure 會把它們當成_呼叫_。Clojure 預期呼叫會以一個_運算子_開頭,而運算子通常是函式。陣列中其餘的項目則視為_運算元_,也就是說,它們會成為該函式的引數。

Clojure 對陣列的這種特殊處理,正是我們無法直接建立陣列字面值的原因。用quote或其簡寫'把陣列標記起來,就表示這個陣列不應該被求值。

和某些現代語言不同,Clojure 的陣列是_異質_的,也就是說,它們內部可以包含多種不同型別的項目,例如'(2 "a" "b" 3)。

和其他 Lisp 不同,Clojure 中的空陣列為真,並不等同於nil或false。

說明

Elyse 非常期待在即將到來的拉斯維加斯之旅中玩幾局撲克(以及其他紙牌遊戲)。她非常熱衷於「自我追蹤」,因此想寫幾個小函式來幫她處理追蹤的工作,並請你幫忙一起想想該怎麼做。

1. 追蹤撲克牌局

Elyse 特別喜歡撲克,她想記錄自己玩了幾局,以及_玩了哪些牌局_。每一局都有專屬的編號,而每張牌桌都會顯示目前正在進行的牌局編號。Elyse 選了一張牌桌坐下,開始玩她的第一局。她打算玩三局。

實作一個函式rounds,它接受目前的牌局編號,並回傳一個list,內容包含該局以及_接下來兩局_的編號:

(rounds 27)
;;=> (27 28 29)

2. 把所有牌局記在同一個地方

Elyse 在第一張牌桌玩了幾局,然後休息一下,又到第二張牌桌玩了幾局……結果每張牌桌各得到一份不同的陣列!她想把這兩份陣列合併起來,這樣就能把所有撲克牌局記錄在同一個地方。

實作一個函式concat-rounds,它接受兩個陣列,並回傳一個list,內容是第一個list裡的所有牌局,接著是第二個list裡的所有牌局:

(concat-rounds '(27 28 29) '(35 36))
;;=> (27 28 29 35 36)

3. 找出之前的牌局

聊到之前的幾局撲克時,另一位玩家提到其中兩局的過程非常相似。Elyse 不確定自己到底有沒有玩過那幾局。

實作一個函式contains-round?,它接受兩個引數:一個是玩過的牌局陣列,另一個是牌局編號。如果該局在玩過的牌局陣列中,函式會回傳true,不在的話回傳false:

(contains-round? '(27 28 29 35 36) 29)
;;=> true

(contains-round? '(27 28 29 35 36) 30)
;;=> false

4. 計算牌值的平均值

Elyse 想試試一款叫做 Black Joe 的新遊戲。它類似 21 點,目標是讓手上的牌加總達到某個目標點數,不過在 Black Joe 裡,目標是讓牌值的_平均值_等於 7。要算出平均值,可以把所有牌值加總,再除以手牌的張數。

實作一個函式card-average,它會回傳一手 Black Joe 牌的平均值。

(card-average '(5 6 7))
;;=> 6.0

5. 其他的平均值算法

在 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

6. 更多求平均值的方法

平均值實驗的結果讓 Elyse 很感興趣,她想知道取_偶數_位置牌值的平均,和取_奇數_位置牌值的平均,結果會不會一樣。該是再寫一個測試函式的時候了!

實作一個函式average-even-odd?,它會回傳布林值,指出偶數索引位置牌值的平均是否和奇數索引位置牌值的平均相同。

(average-even-odd? '(1 2 3))
;;=> true

(average-even-odd? '(1 2 3 4))
;;=> false

7. 獎勵局規則

在 Black Joe 裡,每第 11 手牌都是獎勵手牌,並附帶一條獎勵規則:如果你抽到的最後一張牌是傑克(11),它的值就要加倍。

實作一個函式maybe-double-last,它接受一手牌,並檢查最後一張牌是不是傑克(11)。如果最後一張牌是傑克(11),就把它的值加倍,再回傳這手牌。

(maybe-double-last '(5 9 11))
;;=> '(5 9 22)

(maybe-double-last '(5 9 10))
;;=> '(5 9 10)
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Clojure Exercism

準備好開始 紙牌遊戲 了嗎?

註冊 Exercism,透過 12 個概念105 個練習 和真人引導來學習並精通 Clojure,全部免費。