Reputation: 8285
I am new to Clojure and it's hard for me to idiomatically implement basic manipulations with data structures.
What would be an 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]
Upvotes: 6
Views: 357
Reputation: 17859
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 (v i) (v j))))
and here is more functional variant to generate all these pairs (it isn't based on 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: following @dg123's answer. it is nice, but you can make it even better, using doseq
's and for
's features like destructuring and guards:
(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 through the tails of a collection, but remember, that iterate
produces an infinite 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) () () ())
so you have to limit it somehow to include just not empty collections.
the destructuring form [x & xs]
splits the argument to a first param and the sequence of the rest params:
user> (let [[x & xs] [1 2 3 4 5 6]]
(println x xs))
1 (2 3 4 5 6)
nil
and when the binded collection is empty, or have a single element, the xs
would be nil
:
user> (let [[x & xs] [1]]
(println x xs))
1 nil
nil
so you just make use of this feature, using :while
guard in a list comprehension.
in the end you just construct pairs (or do some side effect in this case) for x
and every item in xs
Upvotes: 6
Reputation: 6543
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 produces a lazy sequence of the value of each i
index followed by all of its j
s.
You can then iterate over all of the pairs of values you need using for
like so:
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])
Use doseq
if you would like to perform IO instead of producing 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
Upvotes: 3