They almost-always used this idiom of Push, Push, ... Nreverse.
(define (range n) ; returns a list of integers [0 ... n-1]
When i was young, i studied Spice Lisp source code from CMU.
Where are they (Skef, Rob, ...) now?
They almost-always used this idiom of Push, Push, ... Nreverse.
They almost-always used this idiom of Push, Push, ... Nreverse.
For instance, here is a tail-recursive mapcar that explicitly passes the stack of items pushed so far:...
Destructive reverse remains a current technique in manipulation of
cons-based lists. It can make a measurable difference on a 3 GHz
machine just like on a 30 MHz machine.
HenHanna <HenHanna@gmail.com> writes:
They almost-always used this idiom of Push, Push, ... Nreverse.
That is an old school Lisp thing, I think. Scheme tends to frown on mutation, though it has reverse! which can destructively reverse lists
that you explicitly built using cons.
It was before my time but I think historically, mutation (nreverse, rplaca/rplacd, etc.) was more important in machines that were starved
for memory. Once memory got bigger, it tended to get used primarily for caches, images, numerical arrays, and other pointer-free data that
didn't need to be garbage collected, and you didn't have to worry as
much about squeezing the size of your program heap. Another sign of
that change was switching to semispace garbage collectors from intricate BIBOP or similar schemes.
Kaz Kylheku <433-929-6894@kylheku.com> writes:
For instance, here is a tail-recursive mapcar that explicitly passes the
stack of items pushed so far:...
That is Lisp, not Scheme. It's possible that some similar looking code
would work in Scheme, but I notice that Scheme seems to distinguish explicitly consed lists from e.g. quoted lists. reverse! in my
experiments only seems to work on explicitly consed lists.
If the lists are large, I think idiomatically in Scheme one would tend
to use lazy streams (implemented with force and delay) rather than
building and reversing an entire intermediate list. Then you would
generate the mapped elements in order. I expect there are SRFI's for
this but I'm not that familiar with them. Of course in Haskell the
laziness is built into the language.
Destructive reverse remains a current technique in manipulation of
cons-based lists. It can make a measurable difference on a 3 GHz
machine just like on a 30 MHz machine.
As always, justifying such a claim about any particular application
requires benchmarking that application with both reversal methods. The application might not spend much of its time reversing lists.
Even if
it does, the wider implementation is then suspect, because large lists
tend to have poor memory locality. You might look to instead use
generators, or packed vectors, or whatever.
In a well built Common Lisp implementation, all the library functions
that accumulate lists, like mapcar or the collect clause of Loop and
whatnot, can be reasonably expected not to just push conses onto a stack
and then nreverse. Typically, a tail pointer is maintained and the last
cons cell is mutated to add the next one.
There is a way to do this with a single variable. We accumulate
a circular list, keeping a pointer to its tail cons cell
Sure, but that is more work compared to a minor decision of whether
to drop-in replace reverse with nreverse, or other similar decisions
that have to do with "basic tidiness".
NREVERSE is not required to re-use the original cons cells in any
particular way. In fact, NREVERSE can do exactly the same thing as
REVERSE if an implementation determines that there is nothing better
that it can do.
Always use NREVERSE (where you can) because in the unlikely event that
that proves to be the wrong choice, you can _always_ change it to plain REVERSE without worry.
Common Lisp's loop macro has a collect{ing} clause.
(defun iota (n)
(loop for i from i to n
collect n))
On 2/27/2024, Kaz Kylheku wrote:
Common Lisp's loop macro has a collect{ing} clause.
(defun iota (n)
(loop for i from i to n
collect i))
Incorrect; three errors.
Correct:
(defun iota (n)
(loop for i from 0 below n
collect i))
On 2/27/2024, Kaz Kylheku wrote:
Common Lisp's loop macro has a collect{ing} clause.
(defun iota (n)
(loop for i from i to n
collect n))
Incorrect; three errors.
Correct:
(defun iota (n)
(loop for i from 0 below n
collect i))
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 437 |
Nodes: | 16 (2 / 14) |
Uptime: | 187:06:02 |
Calls: | 9,135 |
Calls today: | 2 |
Files: | 13,429 |
Messages: | 6,034,912 |