假设我有这个原子作为游戏的状态:
(defonce state (atom {:player
{:cells [{:x 123 :y 456 :radius: 1.7 :area 10}
{:x 456 :y 789 :radius: 1.7 :area 10}
{...}]}}))
我想读取其中一个单元格的位置,(:cells 向量中的映射 :x 和 :y 值)使用它们应该在下一帧中的那些值进行计算,然后在计算时更新新的 :x 和: y 在相应地图中的位置?
到目前为止我有这个:
(let [cells (get-in @state [:player :cells])]
(reduce
(fn
[seq cell]
(let [cell-x (get-in cell [:x])
cell-y (get-in cell [:y])]
; do my calculations
; ...
))
nil
cells))
所以我可以读取值并进行计算,但是我将如何使用新值更新 x 和 y 位置?我可以使用:
(swap! state update-in [:player :cells ...] assoc :x new-x :y new-y)
但是我不知道索引......在哪个向量中更新它。
我假设有一种不使用 reduce 的方法可以给我索引?
或者我正在接近这个完全不习惯的?
您可以在不知道向量中的索引的情况下更新特定的哈希映射对象:
(let [when-x 123
new-x -1
new-y -1]
(swap! state update-in [:player :cells]
(fn [v] (mapv (fn [{:keys [x y] :as m}]
(if (= x when-x)
(assoc m :x new-x :y new-y)
m))
v))))
;;=> {:player {:cells [{:x -1, :y -1, :radius 1.7, :area 10}
;; {:x 456, :y 789, :radius 1.7, :area 10}]}}
当您有一些可能需要更新许多值的标准时,此代码很有用。请注意,这里我们不需要知道索引来进行更新。
现在稍微进行一下游览,但是如果您只需要更新一个特定的已知索引,那么一种方法是使用相同的技术,但使用map-indexed
而不是mapv
:
(let [when-idx 1
new-x -1
new-y -1]
(swap! state update-in [:player :cells]
(fn [v] (vec (map-indexed (fn [n {:keys [x y] :as m}]
(if (= n when-idx)
(assoc m :x new-x :y new-y)
m))
v)))))
但是,这对您的数据毫无意义,因为向量是一个关联集合,因此update-in
可以按索引进行选择:
(let [when-idx 0
new-x -1
new-y -1]
(swap! state update-in [:player :cells when-idx] #(assoc % :x new-x :y new-y)))
有趣的是,如果你有一个列表而不是一个向量,那么它并不是毫无意义的,所以'({:x 123 :y 456 :radius 1.7 :area 10}{:x 456 :y 789 :radius 1.7 :area 10})
. 对于此非关联集合,您不能使用update-in
.
这种结构不是毫无意义的另一个原因是,如果您担心性能:您可以使用懒惰来短路找到答案:
(defn replace-in [v [idx new-val]]
(concat (subvec v 0 idx)
[new-val]
(subvec v (inc idx))))
(let [when-x 123
new-x -1
new-y -1]
(swap! state update-in [:player :cells]
(fn [v] (->> v
(keep-indexed (fn [idx {:keys [x] :as m}]
(when (= x when-x)
[idx (assoc m :x new-x :y new-y)])))
first
(replace-in v)))))
keep-indexed
与 类似map-indexed
,只是不将任何nil
值返回到输出序列中。一旦该first
值被实现,其余的潜在值就永远不会产生,因此短路。Hereidx
被调用用于subvec
切碎原始向量并包含新的哈希映射对象。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句