I'm learning R5RS Scheme at the moment (from PocketScheme) and I find that I could use a function that is built into some variants of Scheme but not all: Append!
In other words - destructively changing a list.
I am not so much interested in the actual code as an answer as much as understanding the process by which one could pass a list as a function (or a vector or string) and then mutate it.
example:
(define (append! lst var)
(cons (lst var))
)
When I use the approach as above, I have to do something like (define list (append! foo (bar))
which I would like something more generic.
Better late than never for putting in a couple 2-3 cents on this topic...
(1) There's nothing wrong with using the destructive procedures in Scheme while there is a single reference to the stucture being modified. So for example, building a large list efficiently, piecemeal via a single reference - and when complete, making that (now presumably not-to-be-modified) list known and referred to from various referents.
(2) I think APPEND! should behave like APPEND, only (potentially) destructively. And so APPEND! should expect any number of lists as arguments. Each list but the last would presumably be SET-CDR!'d to the next.
(3) The above definition of APPEND! is essentially NCONC from Mac Lisp and Common Lisp. (And other lisps).
Mutation, though allowed, is strongly discouraged in Scheme. PLT even went so far as to remove
set-car!
andset-cdr!
(though they "replaced" them withset-mcar!
andset-mcdr!
). However, a spec forappend!
appeared in SRFI-1. Thisappend!
is a little different than yours. In the SRFI, the implementation may, but is not required to modify the cons cells to append the lists.If you want to have an
append!
that is guaranteed to change the structure of the list that's being appended to, you'll probably have to write it yourself. It's not hard:To keep the definition simple, there is no error checking here, but it's clear that you will need to pass in a list of length at least 1 as
a
, and (preferably) a list (of any length) asb
. The reasona
must be at least length 1 is because you can'tset-cdr!
on an empty list.Since you're interested in how this works, I'll see if I can explain. Basically, what we want to do is go down the list
a
until we get to the lastcons
pair, which is(<last element> . null)
. So we first see ifa
is already the last element in the list by checking fornull
in thecdr
. If it is, we useset-cdr!
to set it to the list we're appending, and we're done. If not, we have to callmy-append!
on thecdr
ofa
. Each time we do this we get closer to the end ofa
. Since this is a mutation operation, we're not going to return anything, so we don't need to worry about forming our modified list as the return value.