I'd like to have a forall
function that acts like for
but takes a list of sequences as inputs.
(forall (fn [arr]
(prn arr))
(range 10)
(range 10)
(range 10))
=>
; [0 0 0]
; [0 0 1]
; [0 0 2]
; ....
; [9 9 9]
Is there a good way to write this?
I suppose you want to map over the cartesian product of the input sequences, right? If so, you can create a lazy sequence of the cartesian product using for example the clojure.math.combinatorics/cartesian-product
function from the clojure.math.combinatorics library and then map over that.
(defn forall [f & seqs]
(map f (apply clojure.math.combinatorics/cartesian-product seqs)))
This is a function that works more or less like the for
macro when there are multiple sequences. Here is an example of calling it, and the result that you would obtain:
(forall (fn [arr]
[:element arr])
(range 3)
(range 3)
(range 3))
;; => ([:element (0 0 0)] [:element (0 0 1)] [:element (0 0 2)] [:element (0 1 0)] [:element (0 1 1)] [:element (0 1 2)] [:element (0 2 0)] [:element (0 2 1)] [:element (0 2 2)] [:element (1 0 0)] [:element (1 0 1)] [:element (1 0 2)] [:element (1 1 0)] [:element (1 1 1)] [:element (1 1 2)] [:element (1 2 0)] [:element (1 2 1)] [:element (1 2 2)] [:element (2 0 0)] [:element (2 0 1)] [:element (2 0 2)] [:element (2 1 0)] [:element (2 1 1)] [:element (2 1 2)] [:element (2 2 0)] [:element (2 2 1)] [:element (2 2 2)])
Note that in your example code, you call the prn
function which is side-effectful, whereas the for
-macro produces a lazy sequence. Combining laziness with side-effects is often discouraged because the side-effect may not happen when you would expect it to happen unless you know how lazy sequences work.
If you don't want to pull in the clojure.math.combinatorics
library, you can easily implement your own cartesian product function, e.g.
(defn cart2 [a b]
(for [x a
y b]
(conj x y)))
(defn my-cartesian-product [& arrs]
(reduce cart2 [[]] arrs))