我是Clojure的新手,我很难习惯用数据结构来实现基本操作。
在Clojure中实现以下代码的惯用方式是什么?
l = [...]
for i in range(len(l)):
for j in range(i + 1, len(l)):
print l[i], l[j]
最佳答案
最简单(但不是大多数FP风格)与您的示例几乎相同:
(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的答案。很好,但是您可以使用
doseq
和for
的功能(例如,解构和防护)来做得更好:(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
当绑定(bind)的集合为空或具有单个元素时,
xs
将为nil
:user> (let [[x & xs] [1]]
(println x xs))
1 nil
nil
因此,您只需在列表推导中使用
:while
保护器就可以使用此功能。最后,您只需为
x
和xs
中的每个项目构造对(或在这种情况下产生一些副作用)