简体   繁体   English

更新原子/作用域

[英]Updating an atom / scope

I'm trying to change the value of an atom which holds information about the state of a game. 我正在尝试更改包含有关游戏状态信息的原子的值。

The code is as follows: 代码如下:

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

I have a function that should update the :pos of the atom, based on the function stored in " :dir ". 我有一个函数应该基于存储在“ :dir ”中的函数来更新原子的:pos

My problem is that in the new-pos function, I get an error saying that basically, @state is nil . 我的问题是在new-pos函数中,我收到一条错误消息,基本上说@state为nil

Error: 错误:

Uncaught TypeError: Cannot read property 'call' of null

What a I missing? 我想念什么?

You are defining your atom before you have declared the go-right function. 在声明go-right函数之前,您正在定义原子。 When you dereference it, you'll get nil . 当您取消引用它时,将得到nil

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

You could re-arrange your code, but I think a better solution would be to use a simpler data type, such as a semantically appropriate keyword. 您可以重新排列代码,但是我认为更好的解决方案是使用更简单的数据类型,例如语义上适当的关键字。

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

Then use it to reference the appropriate function once the atom is dereferenced. 一旦取消引用原子,然后使用它来引用适当的函数。

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

This way you'll be able to serialize your app-state atom, which will allow you to save the state to disk and load it again later. 这样,您将能够序列化您的app-state原子,这将使您可以将状态保存到磁盘并在以后再次加载。

I'd also opt for using a generic movement function, rather than hardcoding for each direction. 我还选择使用通用的运动函数,而不是对每个方向进行硬编码。

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

You probably don't want start calling assoc on the manually deferenced atom either. 您可能也不想在手动引用的原子上开始调用assoc This will result in a need to use reset and you can avoid all of that by using swap in the first place. 这将导致需要使用reset并且可以通过首先使用swap来避免所有这些。

We can remove all of the deref calls and let swap handle them instead. 我们可以删除所有的deref调用,而让swap处理它们。 Functions are generally more useful if they just work with plain data. 如果函数仅使用纯数据,则它们通常会更有用。 Let the lower level dereferencing happen elsewhere. 让较低级别的取消引用发生在其他位置。

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

Finally, to update the state atom, use swap. 最后,要更新状态原子,请使用swap。

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

Complete Code - Works at Clojurescript.net 's REPL 完整代码-在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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM