What is the best way to handle this sequence conversion in Clojure?

I am new to Clojure, and I have been translating some of the data processing work I have done recently as an aid to learning. I have a function translation that works fine, and is shorter, but looks much less readable. Can anyone suggest a more readable and / or more idiomatic way to handle this?

In Python:

def createDifferenceVector(v,startWithZero=True): deltas = [] for i in range(len(v)): if i == 0: if startWithZero: deltas.append(0.0) else: deltas.append(v[0]) else: deltas.append(v[i] - v[i-1]) return deltas 

My attempt to translate Clojure:

 (defn create-diff-vector [v start-zero] (let [ext-v (if start-zero (cons (first v) v) (cons 0 v))] (for [i (range 1 (count ext-v))] (- (nth ext-v i) (nth ext-v (- i 1)))))) 

Maybe this is less readable just because of my inexperience with Clojure, but in particular, the trick of adding an element to the input vector feels to me as if it hides the intention. All the solutions I tried that didn't use the pre-release trick were much longer and uglier.

Many sequence transformations are incredibly elegant in Clojure, but the ones that I still find difficult are ones like this one that a) are manipulated by an index rather than an element, and / or b) require special handling of certain elements.

Thanks for any suggestions.

+8
functional-programming clojure
source share
2 answers

Idiomatic Clojure tends to manipulate sequences in general, rather than individual elements. You can define create-diff-vector in English as:

The result is a vector consisting of:

  • zero or the first input element, depending on whether start-zero true or false; then
  • differences between an input sequence without the first element and an input sequence without the last element.

The second part can be illustrated as follows: to enter (31 41 59 26 53) we have

   input without the first element: (41 59 26 53)
 - input without the last element: (31 41 59 26)
 =================================================== =
   result: (10 18 -33 27)

That, translated into Clojure, becomes remarkably concise:

 (defn diff-vector [v start-zero?] (into [(if start-zero? 0 (first v))] (map - (rest v) v)))) 

A few notes:

  • Question mark at the end of start-zero? serves as a clue that a boolean is expected here.
  • The code uses the fact that the map ping function over sequences of different lengths ends at the end of the shortest sequence.
+12
source share

This implementation will be more idiomatic:

 (defn create-diff-vector [v start-with-zero?] (let [v (cons (if start-with-zero? (first v) 0) v)] (map - (rest v) v))) 

I first add either the first value of the vector, or 0 to the input vector. Then I use map to subtract the vector from myself shifted by one position.

+1
source share

All Articles