3 ms·
The performance difference is not just about TCO, but about creating a new array on each iteration instead of reusing the old one... in Lisp, because it uses co
by brabel 3y ago
The performance difference is not just about TCO, but about creating a new array on each iteration instead of reusing the old one... in Lisp, because it uses cons cells which share structure, appending is very efficient, so the recursive code runs as fast as it can get... not so much with JS arrays.
- pjmlp 3y agoEven in Lisp, if performance matters there are other data structures. We are decades away when lists were the only option.
- brabel 3y agoIn the example in this blog post, I am quite certain plain LISP lists would still be the right choice today.
- odyssey7 3y agoECMAScript continues to evolve and I suspect that there would have been an effort to make the spread operator into a sort of cons in JavaScript. It could have worked for both arrays and objects. However, after seeing that the entry-level bar of proper tail calls couldn’t make it out the door for Chrome and V8, they might have decided not to push it and risk further divergence from a major implementation.
- ColonelPhantom 3y agoIsn't prepending the efficient operation? Appending would require mutation or copying, prepending only requires a single new cons cell. Appending to a linked list is fine if you can mutate and keep a pointer to the tail, but then again, extending mutable arrays is also amortized O(1) assuming a reasonable allocation scheme.
- brabel 3y agoThe common pattern in LISP is to prepend using cons and then reverse the list at the end if order matters. Check this program as an example: http://www.norvig.com/java-lisp.html http://www.norvig.com/java-lisp.html Recursion is done with `(cons (nth-digit digits start) words)` and recursion ends with `(format t "~a:~{ ~a~}~%" num (reverse words))`.
- lispm 3y agoAnother common pattern is to keep track of the last cons and add there. This saves the reverse operation. (loop for i in some-list when (evenp i) collect i) will do something like that behind the scenes.
- brabel 3y agoI don't understand how that works. (reverse) changes every cons cell in order to make each cell point in the "opposite" direction. Do you have a reference I can check?
- lispm 3y agoImagine, we want to write a function which returns a list of numbers from 0 below N. This is just an illustration of the iteration principles. One could use a numeric loop, push each number to the front and return the reversed result list: (defun iota (n &aux result) (dotimes (i n (nreverse result)) (push i result))) But we can also use: (defun iota (n &aux (result (list nil)) (pointer result)) (dotimes (i n (cdr result)) (setf (cdr pointer) (list i) pointer (cdr pointer)))) As you can see, there is no REVERSE needed. Instead the pointer is moved to mark the cons where to add the next cons+item.
- brabel 3y agoOh I see. You keep track of the last cons * on each iteration * :) (this last bit was missing). Interesting, thanks for showing it.