开发者

Using C-style encapsulation techniques in Clojure?

I am working on my first (non-trivial) Clojure program I don't really feel comfortable with how I am declaring all my mutable state globally. Fo开发者_如何学运维r example:

(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))

Since I use C a lot at work I came up with the idea of using common C-style encapsulation techniques. It typically involves struct object that passed as a first argument to any "member functions" that operate on it. See udev for example.

Applying this to Clojure would result in functions that look like this (untested):

(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...

I think it would definitely be a step forward to the global defines that I'm using currently.

So is this the recommended way to go? Or is there a more standard Clojure way?

Update

I'm currently using Clojure 1.1.0.


The basic idea of functional programming is that you have very few global variables but mostly local ones (see Rich Hickey's article on state and identity). Writing a game in this paradigm may be challenging, I'd recommend this post on functional programming for games (the examples are in Erlang, though).

I don't know what kind of game you want to implement. Here is a code snippet improv of what I would do with local variables.

(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))))

You may want to check out another Tetris clone in clojure, it uses opengl, though.


In my Clojure game I'm using a single atom containing a map to store all my mutable game state. This includes certain elements of the user interface state.

This is currently defined as follows:

(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}))

This model is working very well for me. You can easily access the state elements directly e.g. (:game @state), or alternatively define accessor functions.


You can use a map to emulate a C style struct. You can also use (if you are using v1.2) you can use deftype/defrecord.

(defn get-game-score [game]
   (:score game))

(defn set-game-store [game new-score]
   (assoc game :score new-score))

I would recommend using a Map esp. since those can be easily used in multimethods.
The biggest thing to try to keep in mind that is that you should not think about using variables in clojure the same way you do in C and atoms are not the same as variables.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜