用于显示TicTacToe板的功能:它可以简化并且更具可读性吗?

时间:2013-08-21 21:34:55

标签: clojure

当我正在学习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中获益,并用它制作游戏,所以我不能放弃它。)

我想简化它的另一个原因是代码维护和可读性。

提前致谢

3 个答案:

答案 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