Kotlopou
Kotlopou

Reputation: 426

Lisp: Add respective elements of a list of lists

Let's say I have a list:

((1 2 3) (8 4 7) (41 79 30) (0 8 5))

I want to do this:

(1+8+41+0 2+4+79+8 3+7+30+5) = (50 93 45)

I've found an ugly solution:

(defun nested+ (lst)
  (let ((acc nil))
    (dotimes (i (length (first lst)))
      (push (apply #'+ (mapcar #'(lambda (a) (nth i a)) lst)) acc))
    (reverse acc)))

It seems to work for my purposes, but I guess it is slow and un-lispy. What's the proper way?

Upvotes: 1

Views: 1223

Answers (3)

Gwang-Jin Kim
Gwang-Jin Kim

Reputation: 10010

The naive solution would be

(apply #'mapcar #'+ list)

However, as already pointed out e.g. here by stackoverflow and here by LispWorks, the call-arguments-limit of (in the worst case) 50 arguments applies to functions called by apply. And reduce is suggested instead.

Thus, I suggest:

(defun sum-all (lists)
  (reduce #'(lambda (l1 l2) (mapcar #'+ l1 l2)) lists))

And indeed

(sum-all '((1 2 3) (8 4 7) (41 79 30) (0 8 5)))
;; (50 93 45)

Upvotes: 7

coredump
coredump

Reputation: 38967

Another option is to loop over your list of lists:

(defun sum-all (lists)
  (loop
    for list in lists
    for result = (copy-list list) then (map-into result #'+ result list)
    finally (return result)))

During the first iteration, the first list is copied. The resulting list is then used in successive iterations to hold the respective sums. At the end of the iteration, that result list is returned.

Upvotes: 1

Xach
Xach

Reputation: 11839

One option is (apply #'mapcar #'+ list). Mapcar will consume as many lists as you give it and stop when it reaches the end of the shortest list.

Upvotes: 8

Related Questions