How to sum a list of numbers in Emacs Lisp? - emacs

This works:
(+ 1 2 3)
6
This doesn't work:
(+ '(1 2 3))
This works if 'cl-*' is loaded:
(reduce '+ '(1 2 3))
6
If reduce were always available I could write:
(defun sum (L)
(reduce '+ L))
(sum '(1 2 3))
6
What is the best practice for defining functions such as sum?

(apply '+ '(1 2 3))

If you manipulate lists and write functional code in Emacs, install dash.el library. Then you could use its -sum function:
(-sum '(1 2 3 4 5)) ; => 15

Linearly recursive function (sum L)
;;
;; sum
;;
(defun sum(list)
(if (null list)
0
(+
(first list)
(sum (rest list))
)
)
)

You can define your custom function to calculate the sum of a list passed to it.
(defun sum (lst) (format t "The sum is ~s~%" (write-to-string (apply '+ lst)))
EVAL: (sum '(1 4 6 4))
-> The sum is "15"

This ought to do the trick:
(defun sum-list (list)
(if list
(+ (car list) (sum-list (cdr list)))
0))
[source]
Edit: Here is another good link that explains car and cdr - basically they are functions that allow you to grab the first element of a list and retrieve a new list sans the first item.

(eval (cons '+ '(1 2 3))) -- though not as good as 'reduce'

(insert (number-to-string (apply '+ '(1 2 3))))

Related

Concatenate two lists in Common Lisp

I set myself to the task to write a Common Lisp function that concatenates two lists without using append.
Common Lisp input (concat-lists '(1 2 3) '(4 5 6)) should return (1 2 3 4 5 6)
Even though my solution seems to work it looks overtly complicated
(defun concat-lists(seq1 seq2)
(cond ((not (null seq1)) (cons (car seq1) (concat-lists (cdr seq1) seq2)))
(T (cond ((not (null seq2)) (cons (car seq2) (concat-lists seq1 (cdr seq2))))
(T nil)))))
What I'm looking for is a more elegant solution using reduce where I use seq1 as initial value and then apply a function to each element of seq2, thereby appending each value of the list to seq1. Somehow I always get stuck when trying....
Any help or input is much appreciated. Thanks!
CL-USER 39 > (reduce #'cons
'(1 2 3 4 5)
:initial-value '(a b c d e)
:from-end t)
(1 2 3 4 5 A B C D E)
The solution of Rainer Joswig is really elegant and simple, and respects your request of using reduce.
If you want to see also a recursive, simple solution, then here is the classical one:
(defun concat-lists (seq1 seq2)
(if (null seq1)
seq2
(cons (car seq1) (concat-lists (cdr seq1) seq2))))
(concat-lists '(1 2 3) '(4 5 6))
(1 2 3 4 5 6)
I do understand your requirement for 'reduce'. and here other options:
CL also has 'concatenante'
(concatenate 'list '(1 2 3) '(4 5 6))
There is also the other less complicated (IMHO), and not as elegant.
(defun concat-lists (list1 list2)
(let ((a (copy-list list1))
(b (copy-list list2)))
(rplacd (last a) b)
a))
or
(defun concat-lists (list1 list2)
(let ((a (copy-list list1))
(b (copy-list list2)))
(nconc a b)))

Common lisp: loop through pairs of a list

I have a list who's length is divisible by two, and I'm looking for something similar to the answer to this question:
(loop for (a b) on lst while b
collect (+ a b))
However there is overlap between elements:
(1 2 3 4 5) -> (3 5 7 9)
adding 1 and 2 and then 2 and 3 etc.
Where as I have a list like (1 2 3 4) and am looking for something like
((1 2) (3 4))
as output. Is there a way to make loop step correctly over the list?
Another solution.
Something like this should work:
(let ((list '(1 2 3 4)))
(loop :for (a b) :on list :by #'cddr :while b
:collect (cons a b)))
Also a more verbose variant:
(let ((list '(1 2 3 4)))
(loop :for a :in list :by #'cddr
:for b :in (cdr list) :by #'cddr
:collect (cons a b)))
Another approach using the SERIES package.
See also the user manual from Richard C. Waters.
Setup
(ql:quickload :series)
(defpackage :stackoverflow (:use :series :cl))
(in-package :stackoverflow)
Code
(defun pairs (list)
(collect 'list
(mapping (((odd even) (chunk 2 2 (scan 'list list))))
(list odd even))))
scan the content of list as a "serie"
chunk it with M=2 and N=2:
This function has the effect of breaking up the input series items
into (possibly overlapping) chunks of length m. The starting positions
of successive chunks differ by n. The inputs m and n must both be
positive integers.
More precisely, (chunk 2 2 (scan '(1 2 3 4))) produces #Z(1 3) and #Z(2 4)
mapping in parallel over each odd and even element of those series, produce a series of couples, as done by (list odd even).
finally, collect the result, as a list.
Compilation
All the intermediate "series" are compiled away thanks to a stream-fusion mechanism. Here is the macro expansion when pointing at collect:
(LET* ((#:OUT-1120 LIST))
(LET (#:ELEMENTS-1117
(#:LISTPTR-1118 #:OUT-1120)
(#:COUNT-1113 0)
#:CHUNK-1114
#:CHUNK-1115
#:ITEMS-1123
(#:LASTCONS-1106 (LIST NIL))
#:LST-1107)
(DECLARE (TYPE LIST #:LISTPTR-1118)
(TYPE FIXNUM #:COUNT-1113)
(TYPE CONS #:LASTCONS-1106)
(TYPE LIST #:LST-1107))
(SETQ #:COUNT-1113 1)
(SETQ #:LST-1107 #:LASTCONS-1106)
(TAGBODY
#:LL-1124
(IF (ENDP #:LISTPTR-1118)
(GO SERIES::END))
(SETQ #:ELEMENTS-1117 (CAR #:LISTPTR-1118))
(SETQ #:LISTPTR-1118 (CDR #:LISTPTR-1118))
(SETQ #:CHUNK-1114 #:CHUNK-1115)
(SETQ #:CHUNK-1115 #:ELEMENTS-1117)
(COND ((PLUSP #:COUNT-1113) (DECF #:COUNT-1113) (GO #:LL-1124))
(T (SETQ #:COUNT-1113 1)))
(SETQ #:ITEMS-1123
((LAMBDA (ODD EVEN) (LIST ODD EVEN)) #:CHUNK-1114 #:CHUNK-1115))
(SETQ #:LASTCONS-1106
(SETF (CDR #:LASTCONS-1106) (CONS #:ITEMS-1123 NIL)))
(GO #:LL-1124)
SERIES::END)
(CDR #:LST-1107)))
CL-USER 156 > (loop with list = '(1 2 3 4)
while list
collect (loop repeat 2
while list
collect (pop list)))
((1 2) (3 4))
or
CL-USER 166 > (loop with list = '(1 2 3 4 5 6)
while (and list (cdr list))
collect (loop repeat 2 collect (pop list)))
((1 2) (3 4) (5 6))
CL-USER 167 > (loop with list = '(1 2 3 4 5 6 7)
while (and list (cdr list))
collect (loop repeat 2 collect (pop list)))
((1 2) (3 4) (5 6))

Element not being added to list

(defparameter *todo* '("Conquer the world" "Bake cake"))
(defun how-many-items (list)
if (list
(1+ (how-many-items (cdr list)))
0))
(defun add-item (item)
(cons item *todo*)) ; Attempt to add an item to the todo list
(princ (how-many-items *todo*))
(princ '#\newline)
(add-item "Write a book")
(princ (how-many-items *todo*))
(princ '#\newline)
(princ (cdr *todo*))
(princ '#\newline)
I'm still learning Lisp but I can't understand why the size of the list doesn't add when I supposedly add the item "Write a book" to it, the cdr call returns "Bake Cake" and the number of items is always two.
The output is:
2
2
(Bake cake)
Your problem is that cons is non-destructive. This means that even though you're adding an item to the item to the list that *todo* contains, you're not modifying *todo*
> (defparameter x '(1 2 3))
(1 2 3)
> (cons 1 x)
(1 1 2 3)
> x
(1 2 3)
See? No modification.
Instead use push. It does modify its parameters.
> (defparameter x '(1 2 3))
(1 2 3)
> (push 1 x)
(1 1 2 3)
> x
(1 1 2 3)
You can think of push like this
(push x 1) === (setf x (cons 1 x))
In fact, it's a macro that expands to just this in some implementations.
Your output can't be real, since your function has wrong syntax.
(defun how-many-items (list)
if (list
(1+ (how-many-items (cdr list)))
0))
CL-USER 20 > (how-many-items '(1 2))
Error: The variable IF is unbound.

reduce using cl-loop facility

CL library manual "map over sequences" says "All of these mapping operations can be expressed conveniently in terms of the cl-loop macro" but I don't see how cl-reduce can be expressed in terms of cl-loop
Not sure how "conveniently" expressed it is, but here's my take on it:
(defun loop-reduce (func sequence &rest initial-element)
(loop with result =
(or (car initial-element)
(prog1 (car sequence)
(setf sequence (cdr sequence))))
for x in sequence do (setf result (funcall func result x))
finally (return result)))
(loop-reduce '+ '(1 2 3 4 5))
;; 15
(loop-reduce '+ '(1 2 3 4 5) 10)
;; 25

Name of this function in built-in Emacs Lisp library?

The following Emacs Lisp function takes a list of lists and returns a list in which the items of the inner lists have been concatenated to one big list. It is pretty straight-forward and I am convinced something like this must already be part of the standard function library.
(defun flatten (LIST)
(if LIST
(append (car LIST) (flatten (cdr LIST)))
nil))
I am looking for a function that will take a single list of lists as its argument and then append all the inner lists.
(flatten '((a b) (c d)))
will give
(a b c d)
Does anyone know whether this function is already built in, and if so, under which name?
Thanks!
You're either looking for append:
(defun flatten (list-of-lists)
(apply #'append list-of-lists))
If (and only if) you know that you'll always have a list of lists.
Otherwise:
(defun flatten (list)
(mapcan (lambda (x) (if (listp x) x nil)) list))
Emacs 27.1 has flatten-tree:
(flatten-tree '((a b) (c d)))
(a b c d)
See: https://www.gnu.org/software/emacs/manual/html_node/elisp/Building-Lists.html
I stepped into this only recently whilst looking for something different; there is something that might not have been put into evidence by the test data utilized to check the function, depending on whether the original question was meant to refer to generic lists (i.e.: list of list of list of list of...) or just to two-level lists (as in the example).
The solution based on append works fine only with two-level lists, and there is a further issue with the solution based on mapcan.
Basically, the general solution has to be recursive both on car and cdr, as in the flatten defun below.
(setq l '((((1 2) 3) 4) (5 6 7)))
(defun flatten(x)
(cond ((null x) nil)
((listp x) (append (flatten (car x)) (flatten (cdr x))))
(t (list x))))
(defun flatten2(l)
(if l (append (car l) (flatten2 (cdr l))) nil))
(defun flatten3(l)
(mapcan (lambda(x) (if (listp x) x nil)) l))
(flatten l)
(1 2 3 4 5 6 7)
(apply #'append l)
(((1 2) 3) 4 5 6 7)
(flatten2 l)
(((1 2) 3) 4 5 6 7)
The further issue is with the usage of mapcan in flatten3: as mapcan hides an nconc inside, the user must remember that it alters its argument.
l
((((1 2) 3) 4) (5 6 7))
(flatten3 l)
(((1 2) 3) 4 5 6 7)
l
((((1 2) 3) 4 5 6 7) (5 6 7))
Dash is a modern list library for Emacs, and has flatten. It's the second most downloaded package on Melpa, after magit. From the readme:
-flatten (l): Takes a nested list l and returns its contents as a single, flat list.
(-flatten '((1))) ;; => '(1)
(-flatten '((1 (2 3) (((4 (5))))))) ;; => '(1 2 3 4 5)
(-flatten '(1 2 (3 . 4))) ;; => '(1 2 (3 . 4))
-flatten-n (num list): Flatten num levels of a nested list.
(-flatten-n 1 '((1 2) ((3 4) ((5 6))))) ;; => '(1 2 (3 4) ((5 6)))
(-flatten-n 2 '((1 2) ((3 4) ((5 6))))) ;; => '(1 2 3 4 (5 6))
(-flatten-n 3 '((1 2) ((3 4) ((5 6))))) ;; => '(1 2 3 4 5 6)
This package was started 2012-09.
I realize that the original question was "what is the built in function". It appears that there is none. The other solutions do not actually flatten all lists that I tested. This function appears to work. I'm posting it here because this was the first place Google hit when I did my search.
(defun flatten (LIST)
"flattens LIST"
(cond
((atom LIST) (list LIST))
((null (cdr LIST)) (flatten (car LIST)))
(t (append (flatten (car LIST)) (flatten (cdr LIST))))))
e.g.
(flatten (list "a" (list "b" "c" nil) (list (list "d" "e") "f")))
("a" "b" "c" nil "d" "e" "f")
Have a look at nconc