当我正在学习clojure时,我正在尝试构建一个简单的tic tac toe游戏,而不是ia。我开始使用一种方法来显示一块板,但对我来说这似乎很难看:我正在使用内部函数,以使它们在show-board方法中是本地的,因此它无法在外部实例化。也许是看起来不好的原因。
这是函数(按我的意思工作):
(defn show-board [board]
"Prints the board on the screen.
Board must be a list of three lists of three elements with
:cross for a cross and :circle for a circle, other value for nothing"
(let [convert-elem (fn [elem]
(cond
(= elem :cross) "X"
(= elem :circle) "O"
:other "_"))
convert-line (fn [elems-line]
(reduce str (map convert-elem elems-line)))]
(doseq [line board]
(println (convert-line line)))))
以下是一个用例:
(show-board (list (list :cross :circle :none) (list :none :circle :none) (list :cross :none :none)))
对于丑陋的代码感到抱歉,这是因为我来自Java,而我是从Clojure开始的。 (我想我会从学习Clojure中获益,并用它制作游戏,所以我不能放弃它。)
我想简化它的另一个原因是代码维护和可读性。
提前致谢
答案 0 :(得分:2)
使用内部函数非常好,但在这种情况下使用letfn
可能看起来更好。此外,convert-elem
可以使用case
进行简化,convert-line
可以使用apply
而不是reduce
,原因我在my answer中向Clojure: reduce vs. apply解释3}}问题(简而言之,使用apply
使用单个StringBuilder
,结果流程是线性的;使用reduce
,每个步骤都涉及分配新的StringBuilder
和这个过程是二次的;这并没有太大的区别是这样的小案例,但使用正确的方法仍然是更好的风格。)
这是修改后的功能:
(defn show-board [board]
"Prints the board on the screen.
Board must be a list of three lists of three elements with
:cross for a cross and :circle for a circle, other value for
nothing."
(letfn [(convert-elem [elem]
(case elem
:cross "X"
:circle "O"
"_"))
(convert-line [elems-line]
(apply str (map convert-elem elems-line)))]
(doseq [line board]
(println (convert-line line)))))
作为旁注,这个函数实际上采用了seqables的任意seqable,不一定是列表列表。 Clojure中更常见的具体类型选择是使用向量:
user> (show-board [[:cross :circle :none]
[:none :circle :none]
[:cross :none :none]])
XO_
_O_
X__
nil
答案 1 :(得分:2)
(defn show-board
[board]
(let [convert (fn [el] (get {:cross \X :circle \O} el \_))]
(doseq [line board]
(doseq [el line]
(print (convert el)))
(println))))
答案 2 :(得分:2)
如何简单地让底层编写器缓冲输出而不是创建中间字符串(即使你通过StringBuilder
创建它们)?
=> (defn show-board [board]
(doseq [line board]
(doseq [item line]
(print ({:cross \X :circle \O} item \_)))
(newline)))
如果你想获得一个字符串而不是打印出来,只需使用with-out-str
。