【问题标题】:clojure - update map values in different ways based on key valueclojure - 根据键值以不同方式更新映射值
【发布时间】:2017-02-15 21:36:59
【问题描述】:

我想在 Clojure 中为棋盘游戏表示 2D 位置 + 邻居的图表。我正在使用将位置映射到邻居向量的地图:

{[0 0] [[0 1] [1 0] [1 1]]}

我编写了一些函数,可以为任何大小的棋盘生成邻居图:

(defn positions [size]
  (for [x (range 0 size) y (range 0 size)] [x y]))

(defn neighbors [size [x y]]
  (filter (fn [[x y]]
        (and (>= x 0) (< x size) (>= y 0) (< y size)))
      (-> []
          (conj [(inc x) y])
          (conj [(dec x) y])
          (conj [x (inc y)])
          (conj [x (dec y)])
          (conj [(inc x) (inc y)])
          (conj [(dec x) (dec x)]))))

(defn board-graph
  [size]
  (reduce (fn [map position] (assoc map
                                    position
                                    (neighbors size position)))
          {}
          (positions size)))

这很好用:

(board-graph 2)
=> {[0 0] ([1 0] [0 1] [1 1]), [0 1] ([1 1] [0 0]), [1 0] ([0 0] [1 1] [0 0]), [1 1] ([0 1] [1 0] [0 0])}

然而我现在想在棋盘边缘的每个棋盘位置添加这个额外的“虚拟邻居”,例如:TOP、:BOTTOM、:LEFT、:对。所以我想:

(board-graph 2)
=> {[0 0] (:LEFT :TOP [1 0] [0 1] [1 1]), [0 1] (:LEFT :BOTTOM [1 1] [0 0]), [1 0] (:RIGHT :TOP [0 0] [1 1] [0 0]), [1 1] (:RIGHT :BOTTOM [0 1] [1 0] [0 0])}

到目前为止,这是我的尝试,但效果不太好,而且看起来确实过于复杂:

(defn- filter-keys
  [pred map]
  (into {}
        (filter (fn [[k v]] (pred k)) map)))


(defn board-graph
  [size]
  (let [g (reduce (fn [map position] (assoc map
                                            position
                                            (neighbors size position)))
                  {}
                  (positions size))]
    (merge g
           (reduce-kv #(assoc %1 %2 (conj %3 :TOP)) {}
                      (filter-keys (fn [[x y]] (= y 0)) g))
           (reduce-kv #(assoc %1 %2 (conj %3 :BOTTOM)) {}
                      (filter-keys (fn [[x y]] (= y (dec size))) g))
           (reduce-kv #(assoc %1 %2 (conj %3 :LEFT)) {}
                      (filter-keys (fn [[x y]] (= x 0)) g))
           (reduce-kv #(assoc %1 %2 (conj %3 :RIGHT)) {}
                      (filter-keys (fn [[x y]] (= x (dec size))) g)))))

我基本上想构建我的地图,再次查看它,并为某些键更新关联的值,具体取决于键是什么。如果不诉诸状态,我找不到一个很好的方法! 有没有更惯用的方式来做到这一点?

【问题讨论】:

  • 你熟悉update吗?
  • 谢谢。虽然更新仅适用于单个键。我基本上有 4 个需要调用更新的键列表。考虑到我改变了我的搜索并发现了这个:stackoverflow.com/questions/9638271/…这可能会修复代码的最后一部分。

标签: clojure


【解决方案1】:

您可以在构建返回向量的线程表达式中添加更多表达式。为了更好地阅读,我将它从线程优先宏-&gt; 切换到“线程作为”宏as-&gt;,它将符号(在本例中)c 绑定到每个步骤中线程化的值,以便我可以使用它在某些阶段的条件表达式中:

(defn neighbors [size [x y]]
  (filter (fn [[x y]]
            (and (>= x 0) (< x size) (>= y 0) (< y size)))
          (as-> [] c
              (conj c [(inc x) y])
              (conj c [(dec x) y])
              (conj c [x (inc y)])
              (conj c [x (dec y)])
              (conj c [(inc x) (inc y)])
              (conj c [(dec x) (dec x)])
              (if (zero? x)  (conj c :TOP) c)
              (if (= size x) (conj c :BOTTOM) c)
              (if (zero? y)  (conj c :LEFT) c)
              (if (= size y) (conj c :RIGHT) c))))

每个条件表达式要么返回一个更新的版本,要么在不满足条件的情况下原封不动地通过它。

【讨论】:

  • as-> :我正在寻找类似的东西!谢谢。但是,当遇到 :TOP 之类的关键字时,过滤器将在此处失败。您可以在过滤谓词中添加对关键字的检查以解决此问题。
【解决方案2】:

这对我有用,但与你最初写的不太接近。

(defn positions [size]
  (for [x (range 0 size) y (range 0 size)] [x y]))

(defn neighbors [n [x y]]
  (let [left (if (zero? x)
               :LEFT
               [(dec x) y])
        right (if (= x (dec n))
                :RIGHT
                [(inc x) y])
        up (if (zero? y)
             :TOP
             [x (dec y)])
        down (if (= y (dec n))
               :BOTTOM
               [x (inc y)])]
    (list left right up down)))

(defn board-graph [n]
  (let [keys (positions n)
        vals (map (partial neighbors n) keys)]
    (zipmap keys vals)))

然后

(clojure-scratch.core/board-graph 2)
=>
{[1 1] ([0 1] :RIGHT [1 0] :BOTTOM),
 [1 0] ([0 0] :RIGHT :TOP [1 1]),
 [0 1] (:LEFT [1 1] [0 0] :BOTTOM),
 [0 0] (:LEFT [1 0] :TOP [0 1])}

编辑:正如 Arthur 所指出的,如果您愿意,这不能处理对角线。

【讨论】:

  • 对角线需要更多的案例。
  • 啊错过了。如果您需要处理 8 种不同的事情,我不太喜欢这个解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-08
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
  • 2017-09-29
相关资源
最近更新 更多