在Clojure中实现向量的双重循环的惯用方式是什么?

6

我是Clojure的新手,对于基本数据结构的操作还不太熟悉。

在Clojure中,如何以惯用方式实现以下代码呢?

l = [...]
for i in range(len(l)):
  for j in range(i + 1, len(l)):
    print l[i], l[j]

如前所述,doseq 是一种方法。但是当您想要操作数据时,可能需要使用 mapfor - user5187212
2个回答

6

最简单的(但不是函数式编程中最典型的)实现与您提供的示例几乎完全相同:

(let [v [1 2 3 4 5 6 7]]
  (doseq [i (range (count v))
          j (range (inc i) (count v))]
    (println (v i) (v j))))

这里有一个更加功能强大的变体来生成所有这些对(它不是基于长度或索引,而是基于尾部迭代):

(let [v [1 2 3 4 5 6 7]]
  (mapcat #(map (partial vector (first %)) (rest %))
          (take-while not-empty (iterate rest v))))

输出:

 ([1 2] [1 3] [1 4] [1 5] [1 6] [1 7] [2 3] [2 4] 
  [2 5] [2 6] [2 7] [3 4] [3 5] [3 6] [3 7] [4 5] 
  [4 6] [4 7] [5 6] [5 7] [6 7])

然后在任何副作用中,只需使用这些对:

doseq
(let [v [1 2 3 4 5 6 7]
      pairs (fn [items-seq]
              (mapcat #(map (partial vector (first %)) (rest %))
                      (take-while not-empty (iterate rest items-seq))))]
  (doseq [[i1 i2] (pairs v)] (println i1 i2)))

更新:根据 @dg123 的回答,它很好,但你可以使用 doseqfor 的特性,比如解构和守卫来使它变得更好:
(let [v [1 2 3 4 5 6 7]]
  (doseq [[x & xs] (iterate rest v)
          :while xs
          y xs]
    (println "x:" x "y:" y)))

当你遍历集合的尾部时,要记住iterate会生成一个无限的集合:

user> (take 10 (iterate rest [1 2 3 4 5 6 7]))
([1 2 3 4 5 6 7] (2 3 4 5 6 7) (3 4 5 6 7) 
 (4 5 6 7) (5 6 7) (6 7) (7) () () ())

因此,您必须以某种方式限制它,以仅包括非空集合。解构形式[x&xs]将参数分成第一个参数和其余参数的序列:

user> (let [[x & xs] [1 2 3 4 5 6]]
        (println x xs))
1 (2 3 4 5 6)
nil

当绑定的集合为空或仅有一个元素时,xs 将为 nil

user> (let [[x & xs] [1]]
        (println x xs))
1 nil
nil

所以您只需在列表推导式中使用:while保护来利用此功能。

最后,您只需为xxs中的每个项构建一对(或在这种情况下执行一些副作用)。


1
v 不是向量时,您应该将 (v i) 替换为 (nth v i) - user5187212

3
如何使用mapiterate呢:
user=> (def l [1 2 3 4 5])
#'user/l
user=> (map vector l (iterate rest (drop 1 l)))
([1 (2 3 4 5)] [2 (3 4 5)] [3 (4 5)] [4 (5)] [5 ()])

这将产生一个延迟序列,其中包含每个i索引的值,后跟它的所有j值。

然后您可以使用for循环迭代所需的所有值对,如下所示:

user=> (for [[i js] (map vector l (iterate rest (drop 1 l))) 
             j js] 
         [i j])
([1 2] [1 3] [1 4] [1 5] [2 3] [2 4] [2 5] [3 4] [3 5] [4 5])

如果您想执行IO而不是生成惰性序列,请使用doseq

user=> (doseq [[i js] (map vector l (iterate rest (drop 1 l))) 
               j js] 
         (println (str "i: " i " j: " j)))
i: 1 j: 2
i: 1 j: 3
i: 1 j: 4
i: 1 j: 5
i: 2 j: 3
i: 2 j: 4
i: 2 j: 5
i: 3 j: 4
i: 3 j: 5
i: 4 j: 5
nil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接