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?

Ralph
  • 31,584
  • 38
  • 145
  • 282
  • 1
    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). – Michał Marczyk Apr 23 '10 at 18:50
  • 1
    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 :-). – Ralph Apr 23 '10 at 19:27

4 Answers4

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)
Brian Carper
  • 71,150
  • 28
  • 166
  • 168
  • 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 :-). – Ralph Apr 23 '10 at 20:33
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.

Carl Smotricz
  • 66,391
  • 18
  • 125
  • 167
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)
David Krisch
  • 403
  • 1
  • 5
  • 12
1
(take (* 2 (count suits)) (cycle suits))
Vagif Verdi
  • 4,816
  • 1
  • 26
  • 31