你可以寫一個函數採取的每size
項目chunks
向量從給定的順序,另外一個從前面丟棄這些大塊:
;; note the built-in assumption that s contains enough items;
;; if it doesn't, one chunk less then requested will be produced
(defn take-chunks [chunks size s]
(map vec (partition size (take (* chunks size) s))))
;; as above, no effort is made to handle short sequences in some special way;
;; for a short input sequence, an empty output sequence will be returned
(defn drop-chunks [chunks size s]
(drop (* chunks size) s))
那麼也許增加一個功能,做後兩者(仿照split-at
和split-with
):
(defn split-chunks [chunks size s]
[(take-chunks chunks size s)
(drop-chunks chunks size s)])
假設每個卡最初{:face-up false}
,您可以使用下面的函數把最後一張牌在堆棧上:
(defn turn-last-card [stack]
(update-in stack [(dec (count stack)) :face-up] not))
然後一個函數來從給定的甲板對付出初始堆棧/組塊:
(defn deal-initial-stacks [deck]
(dosync
(let [[short-stacks remaining] (split-chunks 6 5 deck)
[long-stacks remaining] (split-chunks 4 6 remaining)]
[remaining
(vec (map turn-last-card
(concat short-stacks long-stacks)))])))
返回值是一個雙張向量,其第一個元素是甲板的剩餘部分,並且其第二元素是初始堆棧的向量。
然後用這個在交易採取參考到:
(dosync (let [[new-deck stacks] (deal-initial-stacks @deck-ref)]
(ref-set deck-ref new-deck)
stacks))
更重要的是,保持遊戲的整體狀態在一個單一的參考或Atom和ref-set
切換到alter
/swap!
(我「將使用的Ref對於這個例子,省略dosync
並切換到alter
swap!
使用的原子代替):
;; the empty vector is for the stacks
(def game-state-ref (ref [(get-initial-deck) []]))
;; deal-initial-stacks only takes a deck as an argument,
;; but the fn passed to alter will receive a vector of [deck stacks];
;; the (% 0) bit extracts the first item of the vector,
;; that is, the deck; you could instead change the arguments
;; vector of deal-initial-stacks to [[deck _]] and pass the
;; modified deal-initial-stacks to alter without wrapping in a #(...)
(dosync (alter game-state-ref #(deal-initial-stacks (% 0))))
聲明:這一切都不已經接收儘管我認爲它應該可以正常工作,但我可能會錯過任何愚蠢的拼寫錯誤)。不過,這是你的練習,所以我認爲把測試/拋光部分留給你很好。 :-)