简体   繁体   中英

Clojure map. Pass function multiple parameters

I'm looking for a way how to use map function in more custom way. If there is a different function for what I'm trying to achieve, could you please let me know this.

;lets say i have addOneToEach function working as bellow

(defn plusOne[singleInt]
   (+ 1 singleInt))

(defn addOneToEach[intCollection] ;[1 2 3 4]
   (map plusOne intCollection))   ;=>(2 3 4 5)

;But in a case I would want to customly define how much to add 

(defn plusX[singleInt x]
   (+ x singleInt))

(defn addXToEach[intCollection x] ;[1 2 3 4]
   ;how do I use plusX here inside map function?
   (map (plusX  ?x?) intCollection))   ;=>((+ 1 x) (+ 2 x) (+ 3 x) (+ 4 x))

I'm not looking for a function that adds x to each in the collection, but a way to pass extra arguments to the function that map is using.

another option to the already mentioned would be partial (note that in the example the order of the params does not matter, since you just add them, but partial binds them from left to right, so beware):

user=> (doc partial)
-------------------------
clojure.core/partial
([f] [f arg1] [f arg1 arg2] [f arg1 arg2 arg3] [f arg1 arg2 arg3 & more])
  Takes a function f and fewer than the normal arguments to f, and
  returns a fn that takes a variable number of additional args. When
  called, the returned function calls f with args + additional args.
nil
user=> (defn plus-x [x i] (+ x i))
#'user/plus-x
user=> (map (partial plus-x 5) [1 2 3])
(6 7 8)

You almost got it right.

There are several possible ways:

1.

(defn addXToEach[intCollection x]
   (map #(plusX % x) intCollection))

#(%) means same as (fn [x] (x)) (be aware that x is being evaluated here).

2.

(defn addXToEach[intCollection x]
   (map (fn [item] (plusX item x)) intCollection))

3.

(defn addXToEach[intCollection x]
   (map #(+ % x) intCollection))

and then you don't have to define your plusX function.

Hope it helps!

There are several ways to go about it. One is using an explicit local function via letfn :

(defn add-x-to-each [ints x]
 (letfn [(plus-x [i]
           (+ i x))]
   (map plus-x ints)))

For this small piece of code this is probably overkill and you can simply streamline it via an anonymous function:

(defn add-x-to-each [ints x]
  (map #(+ % x) ints))

Both of these solutions basically apply the use of a closure which is an important concept to know: it boils down to defining a function dynamically which refers to a variable in the environment at the time the function was defined. Here we defer the creation of plus-x (or the anonymous) function until x is bound, so plus-x can refer to whatever value is passed in to add-x-to-each .

You are applying map to one collection, so the function that map applies must take one argument. The question is, how is this function to be composed?

The function

(defn plusOne [singleInt]
   (+ 1 singleInt))

... works. It is otherwise known as inc .

But the function

(defn plusX [singleInt x]
   (+ x singleInt))

... doesn't work, because it takes two arguments. Given a number x , you want to return a function that adds x to its argument:

(defn plusX [x]
   (fn [singleInt] (+ x singleInt))

You can use a function returned by plusX in the map .

It is when you compose such a function that you can use extra arguments. This kind of function, composed as an expression involving captured data, is called a closure .

For example, (plusX 3) is a function that adds 3 to its argument.

(map (plusX 3) stuff)
;(4 5 6 7)

As you see, you don't need to name your closure.

Specifically for + the following will also work:

(map + (repeat 4) [3 4 9 0 2 8 1]) ;=> (7 8 13 4 6 12 5)

Of course, instead '4' put your number, or wrap with (let [x 4] ...) as suggested above.

It might not be the most performant, although, I guess.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM