slhsen
slhsen

Reputation: 616

How to evaluate sequence of pure functions in Clojure

My question is pretty similar to this one: How to evaluate a sequence of impure functions in Clojure? but instead of impure functions, how can I evaluate sequence of pure functions and get the results as another sequence?

Let's assume we have a vector of functions like:

[#(str "a" "b") #(str "c" "d") #(str "e" "f")]

And we need an output like this:

("ab" "cd" "ef")

I've tried something similar to this:

(map eval [#(str "a" "b") #(str "c" "d") #(str "e" "f")])

but it just returns a vector of function references.

Upvotes: 2

Views: 352

Answers (5)

user5187212
user5187212

Reputation: 426

Maybe it's worth to have a look at pcalls and/or pvalues

(pcalls #(str "a" "b") #(str "c" "d") #(str "e" "f"))
;=> ("ab" "cd" "ef")

Please note that pcalls is using future inside!

Upvotes: 0

Thumbnail
Thumbnail

Reputation: 13473

Note

As Nathan Davis wrote, eval evaluates a clojure form:

(eval '(+ 1 1)) ;=> 2

What does it do to a function? Nothing!. eval treats functions as literals:

((eval +) 1 1) ;=> 2

For your case:

(def stuff [#(str "a" "b") #(str "c" "d") #(str "e" "f")])

... we have

(= stuff (map eval stuff)) ;=> true

Though

(= [#(str "a" "b") #(str "c" "d") #(str "e" "f")]
   (map eval [#(str "a" "b") #(str "c" "d") #(str "e" "f")]))
;=> false

... since the corresponding functions are different objects, though identical in operation.

That's why eval is useless to you. So follow Magos's advice.

Upvotes: 1

Nathan Davis
Nathan Davis

Reputation: 5766

eval evaluates a Clojure form (i.e., a list containing "code"). You have functions, not forms.

You map with apply, but apply takes both a function and a seq of arguments. Since you have no arguments, you can use the empty vector:

(map apply
     [#(str "a" "b") #(str "c" "d") #(str "e" "f")]
     (repeat []))

But it would be shorter and probably clearer if you use for:

(for [f [#(str "a" "b") #(str "c" "d") #(str "e" "f")]]
  (f))

Upvotes: 1

nberger
nberger

Reputation: 3659

There are multiple ways to do what you are asking. It depends on whether you want a lazy sequence or not (you probably want lazy given there are no side-effects, but you might want no-lazy if there's an intensive computation that you want to cache), or if you want a vector as output (the same as your input). I'll try to follow what you tried to do.

Your map with eval is doing the following with each fn:

user=> (eval #(str 1))
#<user$eval1332$fn__1333 user$eval1332$fn__1333@38747597>

But you want something like the following:

user=> (eval (#(str 1)))
"1"

You want eval to have the fn applied, that is: the fn should be the first element of a list. Let's put it in a list:

user=> (map (comp eval list) [#(str "a" "b") #(str "c" "d") #(str "e" "f")])
("ab" "cd" "ef")

Cool. But instead of eval, you probably want to use apply:

user=> (apply #(str 1))
; ArityException Wrong number of args (1)

Ups! It failed. apply doesn't have a 0-arity overload, but we can pass an empty list:

user=> (apply #(str 1) ())
"1"

Much better. Let's do it with map:

user=> (map #(apply % ()) [#(str "a" "b") #(str "c" "d") #(str "e" "f")])
("ab" "cd" "ef")

Or better yet, given your functions receive no arguments, you'd better do as suggested by @Magos:

user=> (map #(%) [#(str "a" "b") #(str "c" "d") #(str "e" "f")])                                                                              
("ab" "cd" "ef")

Upvotes: 9

Magos
Magos

Reputation: 3014

You could use (fn [f] (f)) or #(%) in your map.

Upvotes: 5

Related Questions