Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In clojure, how to apply a function to selected items in a [large] vector

Tags:

clojure

matlab

I have a vector v

(def v [1 2 5 8 4 3 8 9 3])

I want to apply function myfn

(defn myfn [x] (+ 1 x))

to selected items that I have their indexes idx

(def idx [3 5])

I've seen How do I modify a portion of a vector in Clojure? and it is not exactly what I need.

Like what you do in MATLAB

v = [1 2 5 8 9 3];
idx = [3 5];
v(idx) = myfn(v(idx));
like image 428
Ali Avatar asked Nov 20 '11 02:11

Ali


3 Answers

Vectors in clojure are associative, so you can do something like this: (reduce #(update-in %1 [%2] myfn) v idx)

like image 55
mange Avatar answered Oct 21 '22 01:10

mange


Updated because I misinterpreted the question.

Here's another solution:

(apply assoc v (mapcat #(vector % (myfn (v %))) idx))

that is, build up an argument list of index/new-value pairs to assoc. I think mange's solutions is probably better though.


Original, incorrect solution

Don't forget that vector v is itself a function of its indices. So:

(map myfn (map v idx))

or:

(->> idx (map v) (map myfn))

or:

(map (comp myfn v) idx)

I'm sure there's also a very clever answer involving juxt :)

like image 30
Dave Ray Avatar answered Oct 21 '22 00:10

Dave Ray


You mention "a [large] vector", so do you care about performance? You may want to find out about transients:

(persistent!
  (reduce (fn [v i] (assoc! v i (myfn (get v i))))
          (transient v)
          idx))

Or, if you prefer looping style, this does the same thing:

(loop [v (transient v), [i & is :as idx] idx]
  (if (empty? idx)
    (persistent! v)
    (recur (assoc! v i (myfn (get v i))) is)))
like image 2
Jouni K. Seppänen Avatar answered Oct 21 '22 02:10

Jouni K. Seppänen