I was trying to familiarize myself with several languages by doing the advent of code exercises in these languages, and one that got me stuck is Emacs Lisp, in the very first exercise.
The goal is to count, given a certain list of numbers, the number of consecutive increasing numbers in the list. Assume for the rest of this post input
is the list we want to process, which contains 2000 numbers (so it's a relatively small list).
My first solution was maybe not so idiomatic, because it was inspired by a previous solution in a other language (basically a translation).
It's
(require 'dash)
(defun translated-solution (vals)
(-sum
(-zip-with
(lambda (x y) (if (< y x) 1 0))
vals (cdr vals))))
Basically it duplicates the list, skips the first element of the copy, then compares the two versions element-wise, summing each time the second element is greater than the first one. It works fine.
My second solution is
(require 'cl)
(defun recursive-solution (vals)
(let ((x (car vals))
(y (cadr vals))
(tail (cddr vals)))
(if (and x y)
(+
(if (< x y) 0 1)
(recursive-solution (cons y tail)))
0)))
This works on small lists, but makes Emacs hard-freeze when testing on input
, forcing me to kill -9
...
I understand this could be caused by a stack overflow, due to recursing too much, but I don't understand why would that cause Emacs to freeze. If it were the case, I would expect Emacs either to crash, or to take back control over the interpreter resulting in an error.
Anyways, I tried a tail-recursive version:
(defun tail-recursive-solution (vals)
(flet ((aux (vals s)
(let ((x (car vals))
(y (cadr vals))
(tail (cddr vals)))
(if (and x y)
(aux (cons y tail) (if (> y x) (1+ s) s))
s))))
(aux vals 0)))
but get aux: Variable binding depth exceeds max-specpdl-size
. This looks like a preemptive error to a stack overflow, due to the fact that Emacs lisp probably allows a binding-depth that is smaller than the stack size. But this seems like the tail recursion optimization has not been made, and I don't understand why.
So, my question is: why do these simple functions on such a small input make Emacs go crazy? What is the idiomatic way to solve this exercise?