What is an idiomatic way to implement a double loop over a vector in Clojure?

I am new to Clojure, and it’s hard for me to idiomatically implement basic manipulations with data structures.

What would be the idiomatic way to implement the following code in Clojure?

l = [...] for i in range(len(l)): for j in range(i + 1, len(l)): print l[i], l[j] 
+6
source share
2 answers

the simplest (but not the most FP-ish) is almost identical to your example:

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

and here is a more functional option for generating all these pairs (it does not depend on the length or indices, but rather on the tail iteration):

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

output:

  ([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]) 

then just use these pairs in doseq for any side effect:

 (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))) 

update: after @ dg123 answer. this is nice, but you can do it even better by using doseq and for functions such as destructuring and protection:

 (let [v [1 2 3 4 5 6 7]] (doseq [[x & xs] (iterate rest v) :while xs y xs] (println "x:" x "y:" y))) 

you iterate over the tails of the collection, but remember that iterate creates endless code:

 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) () () ()) 

so you need to somehow limit it to including only empty collections. the destructive form [x & xs] divides the argument into the first pair and the sequence of other parameters:

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

and when the linked collection is empty or has one element, xs will be nil :

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

so you just use this function using :while guard in list comprehension.

in the end you just create pairs (or do some side effects in this case) for x and each element in xs

+4
source

How about using map vector and iterate :

 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 ()]) 

which creates a lazy sequence of values ​​for each index i , followed by all its j s.

Then you can iterate over all the pairs of values ​​you need using for as follows:

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

Use doseq if you want to do IO instead of creating a lazy sequence:

 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 
+2
source

All Articles