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

在Clojure中实现向量双循环的惯用方法是什么?,clojure,Clojure,我是Clojure的新手,很难习惯性地用数据结构实现基本操作 在Clojure中实现以下代码的惯用方法是什么 l = [...] for i in range(len(l)): for j in range(i + 1, len(l)): print l[i], l[j] 最简单的(但不是最简单的)与您的示例几乎相同: (let [v [1 2 3 4 5 6 7]] (doseq [i (range (count v)) j (range (inc i)

我是Clojure的新手,很难习惯性地用数据结构实现基本操作

在Clojure中实现以下代码的惯用方法是什么

l = [...]
for i in range(len(l)):
  for j in range(i + 1, len(l)):
    print l[i], l[j]
最简单的(但不是最简单的)与您的示例几乎相同:

(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))))
下面是生成所有这些对的更具功能性的变体(它不是基于长度或索引,而是基于尾部迭代):

输出:

 ([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的答案。这很好,但您可以使用
doseq
进行解构和保护等功能,使其变得更好:

(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
会生成一个无限的coll:

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
保护


最后,您只需为
x
xs
中的每一项构建成对(或在本例中产生一些副作用),使用
映射向量和
迭代如何:

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
s

然后,您可以使用
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

您应该将
(vi)
替换为
(n次vi)
(当
v
不是向量时)。正如前面提到的
doseq
是一种方法。但当您想要操作数据时,您可能需要的是
map
for
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