Vector Clojure;如何迭代映射向量,以便索引已知,以便能够在映射中更新?

Vector Clojure;如何迭代映射向量,以便索引已知,以便能够在映射中更新?,vector,clojure,hashmap,Vector,Clojure,Hashmap,假设我把这个原子作为游戏的状态: (defonce state (atom {:player {:cells [{:x 123 :y 456 :radius: 1.7 :area 10} {:x 456 :y 789 :radius: 1.7 :area 10} {...}]}})) 我想读取其中一个单元格的位置,(在:c

假设我把这个原子作为游戏的状态:

(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}]}}
当您有一些可能需要更新许多值的标准时,此代码非常有用。注意,在这里我们不需要知道索引就可以进行更新

现在进行一次轻微的偏移,但是如果您只需要更新一个特定的已知索引,那么一种方法就是使用相同的技术,但是使用
地图索引
,而不是
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)))))
但是,这对于您的数据来说毫无意义,因为向量是一个关联集合,因此
updatein
将能够按索引选择:

(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)))
有趣的是,如果不是向量而是一个列表,那么它并不是毫无意义的,
({:x123:y456:radius 1.7:area 10}{:x456:y789:radius 1.7:area 10})
。对于此非关联集合,您不能在中使用
更新

这种构造并非毫无意义的另一个原因是,如果您担心性能:您可以利用惰性来缩短寻找答案的时间:

(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
值都不会返回到输出序列中。一旦实现了
第一个
值,则不会产生剩余的电位值,因此短路。这里,
idx
用于调用
subvec
来切碎原始向量并包含新的哈希映射对象。

您是否检查了specter?它对这类东西有很好的支持:谢谢你提供了这些选择!换句话说,没有clojure核心函数,它允许我通过给我迭代的索引来“迭代”向量?是的。您所描述的正是
映射索引的功能。