Clojure 更新原子/作用域

Clojure 更新原子/作用域,clojure,clojurescript,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] (a

我试图改变一个原子的值,它保存着关于游戏状态的信息

代码如下:

(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
”中的函数更新atom的
:pos

我的问题是,在
newpos
函数中,我得到一个错误,即@state基本上是
nil

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

未捕获类型错误:无法读取null的属性“call”


我遗漏了什么?

在声明
向右运行
函数之前,您正在定义atom。当您取消引用它时,您将得到
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)))
通过这种方式,您将能够序列化
应用程序状态
atom,这将允许您将状态保存到磁盘,并在以后再次加载

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

(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]) })
您可能也不想在手动延迟的atom上开始调用
assoc
。这将导致需要使用
reset
,您可以通过首先使用
swap
来避免所有这些

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

(defn update-state [app-state] 
  (assoc app-state :pos (new-pos app-state)))
最后,要更新原子状态,请使用swap

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

完整的代码-在的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)

我们需要更多的代码。什么是
交换呼叫看起来像什么?
右转的源代码是什么?错误是什么?在新pos功能上,它在交换前断开。
:dir
的值是否可能是
nil
右转
在地图上的位置是否为零?我知道Clojure,但不知道Clojurescript的不同之处。我不知道我的建议是否可信。我同意这一点,但也注意到(不是重新排列代码),您还可以使用。