apply function with multiple parameter with arguments from a vector

I have a function that takes three arguments say somefunction [param1 param2 param3] and a vector with values say [val1 val2 val3 val4 ...] I would like to repeatedly call the somefunction with the first argument param1 as fixed and the other two parameters passed in the combination val1 val2, val2 val3, val3 val4 ... etc. which is equivalent to (somefunction sampleparam1 val1 val2) (somefunction sampleparam1 val2 val3) (somefunction sampleparam1 val3 val4)... etc. IS there a way to elegantly do this in clojure?

Upvotes: 1

Views: 2005

Answers (3)

Mark Fisher
Mark Fisher

Reputation: 9886

I think I get the gist of your question.

;; a function that returns the vector as pairs of sub-vectors
(defn pairs [v]
  (map vector v (rest v)))

This splits your vector [val1 val2 val3...] into a sequence of pairs as you wanted.

Example output from this is

(pairs [1 2 3])
=> ([1 2] [2 3])

As @Kyle has pointed out you can also use (partition 2 1 v) here too, as you're only really interesting in sequences, not actual vectors as they get squashed later into parameters via apply.

Next, your function needs to do something. For illustration in the output, I'll just return a map with params as values to the keys :a :b :c

(defn somefn [p1 p2 p3]
  {:a p1 :b p2 :c p3})

now create a new function that calls your function, but with a constant first arg. I'll just use :p1 as a marker. Thus you only have to call (partial-somefn p2 p3) which will call (somefn :p1 p2 p3)

(def partial-somefn (partial somefn :p1))

then to call it with pairs from your vector (or sequence)...

either use map...

(map #(apply partial-somefn %) (pairs [1 2 3]))
=> ({:b 1, :c 2, :a :p1} {:b 2, :c 3, :a :p1})

or use doseq for side effects...

(doseq [ps (pairs [1 2 3])]
  (apply partial-somefn ps))

or a for loop

(for [ps (pairs [1 2 3])]
  (apply partial-somefn ps))
=> ({:b 1, :c 2, :a :p1} {:b 2, :c 3, :a :p1})

You can see that the maps returned show the parameters were called in sequence with the constant first parameter.

So the condensed version is

(defn somefn [p1 p2 p3]
  ;; your code here
  )

(def v [:v1 :v2 :v3])
(let [pairs (fn [v] (map vector v (rest v)))
      pf    (partial somefn :param1)]
  (map #(apply pf %) (pairs v)))

or use one of the for or doseq variations depending on what somefn does

Upvotes: 1

Frank C.
Frank C.

Reputation: 8096

Vinoth, there is:

You are first asking to create a function that takes a maximum of three (3) arguments: [param1 param2 param3] but you want to be able to fix the first parameter. In addition, while you have stated that you have a vector of arguments it appears, from your write up, that you want to go through the vector of values such that the first call to somefunction takes the 1st and 2nd item from the vector, the second call takes the 2nd and 3rd item from the vector, and so on until the vector is exhausted.

The first part of your question (fixed first parameter) can be solved with:

(partial somefunction sampleparam1)

The second part is a bit more complicated and without more details I can only guess. But here is a small demonstration of one approach:

(defn somefunction
  [param1 param2 param3]
  (+ param1 param2 param3))

(def part (partial somefunction 100))

(let [x [1 2 3 4 5]
      y (first x)
      z (second x)]
  (part y z))

If you could explain more about

  1. Are the value vectors consistent in length?
  2. What does somefunction do?

My first thought went to using reduce or loop but I hesitate to assume too much.

Upvotes: 1

Kyle
Kyle

Reputation: 22278

I'm not sure what you want to do with the results of each call to somefunction so I'll assume it is executed for its side-effects and use doseq.

(let [f (partial somefunction param1)]
  (doseq [args (partition 2 1 [val1 val2 val3 val4])]
    (apply f args)))

clojure.core/partition:

(partition 2 1 [1 2 3 4]) ;; ((1 2) (2 3) (3 4))

clojure.core/partial

(def plus10 (partial + 10))
(plus10 5) ;; 15

clojure.core/apply

(apply + [1 2 3]) ;; 6

Note: you can get away without using partial since apply accepts intervening arguments: (apply somefunction param1 args)

Upvotes: 2

Related Questions