Clojure-How to add successive pairs in vector?

2019-02-24 02:00发布

问题:

Trying to write a recursive function that adds successive pairs in a vector.

[1 2 3 4] => [3 5 7]

Pretty much stuck & this is what I have currently:

(def tmp  [ 1 2 3 4])

user> (map #(+ (second %) (first %)) (partition-all 2 tmp ))

This is wrong as it adds just the pairs & not successive pairs. I get[3 7] instead of [3 5 7]

回答1:

Here is another possible solution:

(def tmp [1 2 3 4])

(map + tmp (rest tmp))


回答2:

Partition takes an extra argument specifying how far to step forward between each partition.

(map #(apply + %) (partition 2 1 [1 2 3 4 5])) =>

(3 5 7 9)



回答3:

You are asking for a recursive function, and even though in most cases set and list functions are preferable above crafting your own recursive function, this is not always possible. This problem is also very easy to write recursively in a idiomatic functional way, using an accumulator

The idea is you start with an empty accumulator, and build your result step by step. The accumulator (acc in this code snippet) will hold the answer in the end.

(defn add-pairs [v1]
  (loop [the-list v1  acc []]
    (if   (next the-list) ;; checks wether there's more than 1 left in the-list
      (recur (rest the-list ) 
             (conj  acc (+ (first the-list) (second the-list))))
     acc)))

What happens here? You pass vector [1 2 3 4] to v1. Then the loop starts and in it , two variables are initialized :

the-list <- [1 2 3 4]
acc []

Simply enough the sum of the first two elements (+ (first the-list) (second the-list) are added to the accumulator. Then with recur the loop is called again passing the rest of the list (rest the-list) and the accumulator, now holding 3 (being the sum of the two first elements). The new vaues are :

the-list <- [2 3 4]
acc [3]

next steps :

the-list <- [3 4]
acc [3 5]

the-list <- [4]
acc [3 5 7]

Then the if condition no longer holds and the else clause is returned (conveniently being the accumulator, now holding the solution).

General idea

You start out with an empty accumulator and your input set. Each step, the accumulator builds up. In most cases, the input set is getting smaller each step too. Then some if-condition is met and the accumulator returns.

Also very classic in this particular problem is that the input set is loosing its first element each step. (rest ) is very practical here ( cdr in most other lisps).

general form

The loop/recur form in clojure is very nice, but in a lot other languages, this syntax is lacking. A very classical solution is to have two functions : one recursive that does the looping, and one with the same name, but other arity that does the initialization of the accumulator. Simpler in code than it is to explain, so here it is in some made up java-like syntax :

function add-pairs(v1 acc) {
   if (cond)
      //do stuff to v1 and acc
      add-pairs(v1 acc)
   else 
      return acc }

function add-pairs(v1) {
   return add-pairs(v1,[])
}

var result = add-pairs([42 666 1447])