Repeating vectors in Clojure
Asked Answered
I

4

6

I am a Clojure newbie. I am trying to get two copies of a vector of card suits. The non-DRY way that I can come up with is

(def suits [:clubs :diamonds :hearts :spades])
(def two-times (concat suits suits))

There must be a more functional way (even if it takes more characters :-)). What if i want N times? Any suggestions?

All of the things I try, like

(replicate 2 suits)

results in two separate vectors:

([:clubs :diamonds :hearts :spades] [:clubs :diamonds :hearts :spades])

How do I "flatten" the structure?

Inadvertent answered 23/4, 2010 at 18:34 Comment(2)
There's nothing non-functional about using the value of one Var inside the def form creating another Var. In fact, that's the natural thing to do if the second Var depends on the first. Of course if you want a general method of concatenating n copies of a seq, where n might or might not be known beforehand, then you do need a better solution (as found in the answers here).Lemaster
Actually, that's what I meant (N copies). I agree that my two-times function is "functional" -- it is just not practical if I want 100 copies :-).Inadvertent
W
8

concat gives you a lazy seq. If you want to end up with a (non-lazy) vector instead:

user> (into suits suits)
[:clubs :diamonds :hearts :spades :clubs :diamonds :hearts :spades]
user> (reduce into (replicate 2 suits))
[:clubs :diamonds :hearts :spades :clubs :diamonds :hearts :spades]

Depending whether you're accessing this by index a lot or iterating over it, either a vector or a seq might be more appropriate.

There's always cycle too, if you want an endless (lazy) stream of repeated elements:

user> (take 9 (cycle suits))
(:clubs :diamonds :hearts :spades :clubs :diamonds :hearts :spades :clubs)
Welt answered 23/4, 2010 at 19:6 Comment(1)
I also came up with (take 8 (cycle suits)). Seems to work, but I had to figure the "8" part out myself, without the help of a computer :-).Inadvertent
P
2

(untested!)

(apply concat (repeat 2 suits))

will hopefully do the trick.

concat will of course concatenate 2 lists; apply can be used to smuggle a given function into the head position of an existing list for evaluation.

Phonetist answered 23/4, 2010 at 18:37 Comment(3)
Fixed the function name (assoc -> apply).Lemaster
Oh, how stupid of me. I use apply all the time, you'd think I'd know its name by now... Thanks for the fix!Phonetist
(doc replicate) as of Clojure 1.7 says DEPRECATED: Use 'repeat' instead.Wright
W
2

A little experimentation with the REPL lead me to this solution:

user=> (def suits [:clubs :diamonds :hearts :spades])
#'user/suits
user=> suits
[:clubs :diamonds :hearts :spades]    
user=> (reduce concat (replicate 2 suits))
(:clubs :diamonds :hearts :spades :clubs :diamonds :hearts :spades)
Wig answered 23/4, 2010 at 18:46 Comment(0)
B
1
(take (* 2 (count suits)) (cycle suits))
Byplay answered 24/4, 2010 at 18:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.