更新原子/作用域
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)
我正在尝试更改保存有关游戏状态信息的原子的值。
代码如下:
(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)