Reputation: 9051
I'm new to Clojure and I tried to play with the example data from clojuredocs.org.
;; Data
(def scenes [{:subject "Frankie"
:action "say"
:object "relax"}
{:subject "Lucy"
:action "loves"
:object "Clojure"}
{:subject "Rich"
:action "tries"
:object "a new conditioner"}])
(defn play [scenes n]
"Play a scene"
(->>
scenes
(#(get % n))
((juxt :subject :action :object))
(interpose " ")
(apply str)))
The play
function works fine:
my-stuff.core> (play scenes 0)
"Frankie say relax"
my-stuff.core> (play scenes 1)
"Lucy loves Clojure"
my-stuff.core> (play scenes 2)
"Rich tries a new conditioner"
This play-all
function doesn't work:
(defn play-all [scenes]
"Play all the scenes"
(let [x (count scenes)]
(for [n (range x)]
(map play scenes n ))))
How to correct this play-all
function, i.e. how to apply the play function to the range of data?
Upvotes: 1
Views: 841
Reputation: 13473
Before we do anything with it, your play
function should deal with a single scene:
(defn play [scene]
"Play a scene"
(->> scene
((juxt :subject :action :object))
(interpose " ")
(apply str)))
You use it like so:
(play (scenes 0))
;"Frankie say relax"
... which is no easier or harder than before. But
It also makes play-all
simpler:
(defn play-all [ss]
(map play ss))
(play-all scenes)
;("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")
I was tempted to replace ((juxt :subject :action :object))
with vals
in play
, but we can't rely on the sequence order of the map entries.
Upvotes: 0
Reputation: 6033
map
iterates over a collection (or several collections) to produce a sequence.
for
build a sequence from a list comprehension.
In your case you can use either one or the other.
In terms of decomposition, it would make sens to have actually a function that plays one scene :
(defn play-one [scene]
"Play a scene"
(->>
scene
((juxt :subject :action :object))
(interpose " ")
(apply str)))
Then playing the nth can use precedent definition:
(defn play-nth [scenes n]
"Play the n(th) scene"
(->
scenes
(#(get % n))
play-one))
And you have several ways to play all the scenes:
(defn play-all-map1 [scenes]
"Play all the scenes"
(map (partial play-nth scenes) (range (count scenes))))
But you can really simplify since you don't need range
because scenes
can be treated as a sequence (assuming you are not interested in the index):
(defn play-all-map2 [scenes]
"Play all the scenes with map"
(map play-one scenes))
And with for
:
(defn play-all-for [scenes]
"Play all the scenes with for"
(for [scene scenes]
(play-one scene)))
Upvotes: 0
Reputation: 222198
You don't need both for
and map
.
With only for
:
user=> (defn play-all [scenes]
#_=> "Play all the scenes"
#_=> (let [x (count scenes)]
#_=> (for [n (range x)]
#_=> (play scenes n ))))
#'user/play-all
user=> (play-all scenes)
("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")
and with only map
:
user=> (defn play-all [scenes]
#_=> "Play all the scenes"
#_=> (let [x (count scenes)]
#_=> (map #(play scenes %1) (range x))))
#'user/play-all
user=> (play-all scenes)
("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")
(I prefer the latter.)
Edit: If you like ->>
, this is even better:
user=> (defn play-all [scenes]
#_=> "Play all the scenes"
#_=> (->> scenes
#_=> (count)
#_=> (range)
#_=> (map #(play scenes %))))
#'user/play-all
user=> (play-all scenes)
("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")
Upvotes: 2