更新原子/作用域

Updating an atom / scope

我正在尝试更改保存有关游戏状态信息的原子的值。

代码如下:

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

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

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

错误:

Uncaught TypeError: Cannot read property 'call' of null

我错过了什么?

您在声明 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)))

最后,要更新状态原子,请使用交换。

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