Clojure的;如何遍历地图矢量,以便知道索引,以便能够在地图中更新?

时间:2017-08-08 07:10:04

标签: 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}
                                {...}]}}))

我想要读取其中一个单元格(地图: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来提供索引吗?

或者我是否正在接近这种完全不正常的事情?

1 个答案:

答案 0 :(得分:1)

您可以在不知道向量中的索引的情况下更新特定的哈希映射对象:

(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-indexedmap-indexed类似,只是任何nil值都不会返回到输出序列中。一旦实现first值,就不会产生其余的潜在值,因此会产生短路。调用idx时,subvec会使用namespace App\Http\Controllers;来剪切原始矢量并包含新的哈希映射对象。