Reputation: 11329
I'm having trouble figuring out how to abstract some simple code into a loop/map/for structure. I have the following code which works, in the sense that it gives me the output I want:
(let
[recipe [1 1 2]
voicing [0 2 4]
counts (range (count voicing))
scale C]
(map vector
(make-scale recipe voicing scale 0)
(make-scale recipe voicing scale 1)
(make-scale recipe voicing scale 2)
)
the output is:
([:C :E :G] [:D :F :B] [:E :A :C] [:G :B :D] [:A :C :F] [:B :E :G] [:D :F :A])
I'm basically using the "(map vector arg1 arg2...)" call to interleave 3 seqs.
obviously I need to make the final step of removing the duplicate calls to make-scale. problem is, I need:
(map vector arg1 arg2 arg2)
and all the ways I know how to use a loop give me the results of the loop in a seq:
(map vector (arg1 arg2 arg3))
what's the best way to refactor the intial code, so that I only have a single function call to make-scale?
Upvotes: 4
Views: 176
Reputation: 45071
I think you are looking for:
(apply map vector (map (partial make-scale recipe voicing scale) [0 1 2]))
In general, apply
helps whenever you have a sequence and want to use its elements as consecutive arguments of a function. You can also prepend some arguments to the list (in this case the vector
argument is prepended as the first argment to map
.)
In Clojure many stdlib functions expect an expanded list (vector
and practically every other collection constructor, str
.) I think this is by design - you are expected to use apply
with them.
The sequence in question can even be infinite (i.e. lazy infinite), of course if the function being applied handles lazy infinite argument lists. This works fine, even though concat
is being applied to an infinite list of lists:
(take 7 (apply concat (cycle [[1 2 3]])))
>>> (1 2 3 1 2 3 1)
Upvotes: 3