我正在研究我的第一个(非平凡的)Clojure program我对我如何在全球范围内声明所有可变状态感到不自在。例如:
(def next-blocks (atom []))
(def num-next-blocks 1)
(def is-game-over (atom false))
(def user-name (atom (str)))
(def hs-xml (atom nil))
由于我在工作中使用了很多C,因此我提出了使用常见的C风格封装技术的想法。它通常涉及struct对象,它作为第一个参数传递给对其进行操作的任何“成员函数”。例如,请参阅udev。
将此应用于Clojure将导致函数看起来像这样(未经测试):
(defstruct gamestate)
(defn game-new []
(struct-map gamestate
:level (atom 0)
:score (atom 0)
;etc...
))
(def game-get-score [game]
@(game :score))
(defn game-set-score [game new-score]
(reset! (game :score) new-score))
(defn game-get-level [game]
@(game :level))
(defn game-inc-level [game]
(swap! (game :level) inc))
; etc...
我认为这肯定是我目前正在使用的全球定义的一步。
这是推荐的方式吗?或者是否有更标准的Clojure方式?
我目前正在使用Clojure 1.1.0。
答案 0 :(得分:6)
函数式编程的基本思想是你只有很少的全局变量,但主要是局部变量(参见Rich Hickey's article on state and identity)。在这个范例中编写游戏可能具有挑战性,我建议使用this post on functional programming for games(虽然这些例子在Erlang中)。
我不知道你想要实现什么样的游戏。以下是我对局部变量所做的代码片段改进。
(defn play-game [name score blocks]
(let [level (start-mission (:level score) blocks)]
(if level
(assoc score :level level)
score)))
(defn the-whole-game []
(let [name (ask-username)
score (or (load-score name) {:level 0, :score 0}]
(when-let [new-score (play-game name score [])]
(save-score name new-score))))
您可能需要查看another Tetris clone in clojure,但它使用的是opengl。
答案 1 :(得分:4)
在我的Clojure游戏中,我使用包含地图的单个原子来存储我所有可变的游戏状态。这包括用户界面状态的某些元素。
目前定义如下:
(def state
(atom
{:game (gamefactory/make-game)
:scroll [0 0]
:mouseover [0 0]
:command-state nil
:commands (clojure.lang.PersistentQueue/EMPTY)
:animations {}
:player-id nil}))
这个型号对我来说非常好用。您可以直接轻松访问状态元素,例如(:游戏@state),或者定义访问者功能。
答案 2 :(得分:2)
您可以使用地图来模拟C样式结构。您也可以使用(如果您使用的是v1.2),您可以使用deftype / defrecord。
(defn get-game-score [game]
(:score game))
(defn set-game-store [game new-score]
(assoc game :score new-score))
我建议使用Map esp。因为这些可以很容易地用在多方法中 要记住的最重要的事情是你不应该考虑在clojure中使用变量,就像你在C中使用变量一样,原子与变量不同。