哈希地图的惯用创作

时间:2016-06-10 18:38:27

标签: clojure

我想创建一个散列图,其中 n 以3个为一组创建的键值对,其中集合相交,例如[(34 false) (35 false) (36 false)] && [(24 false) (25 false) (26 false)] -> {34 false 35 false 36 false 24 false 25 false 26 false}

修改

为了与Clojure一起玩/练习,我试图实现战舰棋盘游戏的惯用版本。我决定将战舰坐标存储在哈希映射中,其中键是坐标,值是布尔值,表示该部分是否已被击中。下面的具体代码是假设

  1. 选择轴(水平或垂直)
  2. 选择船首的坐标
  3. "生成"船的其余部分(总共3个坐标)通过相应地增加x或y值,例如{" 10"假" 11"假" 12"假}。注意" 10"转换为矩阵的第二行,第一列。
  4. 注意:在将船舶添加到坐标的哈希图之前,必须检查新的船舶坐标以确保交叉点不存在。如果是这样,则必须重新建造船舶。"
  5. 为此,我已经创建了以下代码。它有两个问题:

    1. 执行该功能会导致使用' acc'累加器:

        

      clojure.lang.LazySeq无法转换为clojure.lang.Associative

    2. 该函数的结果不是单个哈希映射,而是 n 哈希映射列表

    3. 使用惯用语法,我如何实现目标?

      (defn launch
        [n]
        (loop [cnt n acc {}]
          (if (= cnt 0)
            acc
          (recur
            (- cnt 1)
            ((fn []
              (let [axis (rand-int 2)]
                  (if (= axis 0)
                    (let [x (rand-int 8) y (rand-int 10)]
                      (for [k (range 3)]
                        (assoc acc (str y (+ x k)) false)))
                    (let [x (rand-int 10) y (rand-int 8)]
                      (for [k (range 3)]
                        (assoc acc (str (+ y k) x) false)))))))))))
      

2 个答案:

答案 0 :(得分:3)

我将如何重写它:

(defn create-key [axis-val i]
  (if axis-val
    (str (rand-int 10) (+ (rand-int 8) i))
    (str (+ (rand-int 8) i) (rand-int 10))))

(defn launch [n]
  (reduce (fn [acc axis]
            (reduce #(assoc % (create-key axis %2) false)
                    acc
                    (range 3)))
          {}
          (repeatedly n #(zero? (rand-int 2)))))

在repl中:

user> (launch 5)

{"40" false, "07" false, "19" false, 
 "46" false, "87" false, "47" false, 
 "41" false, "62" false, "86" false}

或(如果您不喜欢reduce):

(defn launch [n]
  (zipmap (mapcat #(map (partial create-key %) (range 3))
                  (repeatedly n #(zero? (rand-int 2))))
          (repeat false)))

第三个变体是使用列表推导来生成密钥:

(defn launch [n]
  (zipmap (for [_ (range n)
                :let [axis (zero? (rand-int 2))]
                i (range 3)]
            (create-key axis i))
          (repeat false)))

所有这三个都是惯用的,我猜,所以根据你自己喜欢的编程风格,你可以自己选择一个。

请注意,生成的键会在地图中进行混洗,因为未排序的地图不会保留顺序。如果重要,您应该使用sorted-map

您的变体怎么样,产生错误的一个是:

(for [k (range 3)] (assoc acc (str y (+ x k)) false))

它没有将所有键放到一张地图上,而是生成一个等于(assoc acc k false)的三个项目的序列:

(let [acc {}]
  (for [k (range 3)] (assoc acc k false)))
;;=> ({0 false} {1 false} {2 false})

要做你想做的事,你可以使用reduce

(let [acc {}]
  (reduce #(assoc %1 %2 false) acc (range 3)))
;;=> {0 false, 1 false, 2 false}

答案 1 :(得分:0)

leetwinski给出了一个更简洁的答案,但我想我会发布这个,因为我基本上保持了你的结构完整,这可以帮助你更清楚地看到错误。

首先,我不确定你为什么要重新绑定一个匿名函数调用的值。您的let会愉快地返回结果;所以,你应该考虑为什么你认为有必要创建一个匿名函数。

其次,问题是for返回一个懒惰的seq,并且你将它绑定到你认为的地图数据结构。这解释了为什么它适用于0和1的情况,但是当你使用值2时它会失败。

由于我并不完全了解您要完成的任务,因此这是您的原始代码,经过修改即可使用。免责声明 - 这不是真正的惯用,也不是我写的方式,但是我发帖是因为看起来与原作相比可能会有所帮助,因为它确实有用。

(defn launch
  [n]
  (loop [cnt n
         acc {}]
    (if (= cnt 0)
      acc
    (recur
      (dec cnt)
      (into acc
        (let [axis (rand-int 2)]
          (if (= axis 0)
            (let [x (rand-int 8) y (rand-int 10)]
              (map #(hash-map (str y (+ x %)) false) (range 3)))
            (let [x (rand-int 10) y (rand-int 8)]
              (map #(hash-map (str (+ y %) x) false) (range 3))))))))))