更新原子/范围

时间:2015-12-09 04:34:45

标签: clojure clojurescript

我试图更改包含游戏状态信息的原子的值。

代码如下:

(def initial-state {:pos [0 0] :dir go-right})
(defonce app-state (atom initial-state))

(defn go-right [[x y]] [(inc x) y])

(defn new-pos [state]  ((:dir @state) (:pos @state))))

(defn update-state [app-state] 
  (assoc @app-state :pos (new-pos app-state)))

我有一个函数应根据存储在" :pos"中的函数来更新原子的:dir

我的问题是在new-pos函数中,我收到一条错误,说基本上@state是nil

错误:

Uncaught TypeError: Cannot read property 'call' of null

我错过了什么?

1 个答案:

答案 0 :(得分:4)

在声明go-right函数之前,您正在定义原子。当您取消引用它时,您将获得nil

(def app-state (atom { :dir go-right :pos [0 0] }))
(:dir @state) ;; ===> nil

您可以重新安排代码,但我认为更好的解决方案是使用更简单的数据类型,例如语义上合适的关键字。

(def app-state (atom { :dir :right :pos [0 0] }))

一旦取消引用原子,就用它来引用适当的函数。

(def movement { :right go-right
                :left  go-left
                :up    go-up
                :down  go-down })

(defn new-pos [state]
  (let [dir  (:dir state)
        pos  (:pos state)
        move (get movement dir)]
    (move pos)))

通过这种方式,您可以序列化app-state原子,这样您就可以将状态保存到磁盘并稍后再次加载。

我也选择使用通用移动功能,而不是为每个方向进行硬编码。

(defn move [[dx dy] [x y]]
  [(+ dx x) (+ dy y)])

(def movement { :left  (partial move [-1 0])
                :right (partial move [1 0])
                :up    (partial move [0 1])
                :down  (partial move [0 -1]) })

您可能不想在手动参考原子上开始调用assoc。这将导致需要使用reset,您可以首先使用swap来避免所有这些。

我们可以删除所有deref调用,让swap代替它们。如果函数只使用普通数据,则函数通常更有用。让较低级别的解除引用发生在其他地方。

(defn update-state [app-state] 
  (assoc app-state :pos (new-pos app-state)))

最后,要更新state atom,请使用swap。

(swap! app-state update-state)
;; ===> {:dir :right, :pos [1 0]}

完整代码 - 适用于Clojurescript.net的REPL

(defn move [[dx dy] [x y]]
  [(+ dx x) (+ dy y)])

(def movement { :left  (partial move [-1 0])
                :right (partial move [1 0])
                :up    (partial move [0 1])
                :down  (partial move [0 -1]) })

(defn new-pos [state]
  (let [dir  (:dir state)
        pos  (:pos state)
        move (get movement dir)]
    (move pos)))

(defn update-state [app-state] 
  (assoc app-state :pos (new-pos app-state)))

(def app-state (atom { :dir go-right :pos [0 0] }))

(swap! app-state update-state)