您可以编写一个函数来从给定序列中获取每个项目的chunks
向量,size
并从前面删除这些块:
;; 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)))])))
返回值是一个双元向量,其第一个元素是牌组的剩余部分,第二个元素是初始堆栈的向量。
然后在事务中使用它来考虑 Ref:
(dosync (let [[new-deck stacks] (deal-initial-stacks @deck-ref)]
(ref-set deck-ref new-deck)
stacks))
更好的是,将游戏的整个状态保持在单个 Ref 或 Atom 中并从切换ref-set
到alter
/ swap!
(我将在此示例中使用 Ref,省略dosync
and switch alter
toswap!
以使用原子):
;; 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))))
免责声明:这一切都没有得到丝毫的测试关注(尽管我认为它应该可以正常工作,以我可能错过的任何愚蠢的错别字为模)。不过,这是你的练习,所以我认为将测试/抛光部分留给你很好。:-)