tags:

views:

1168

answers:

1
+3  Q: 

Append! in Scheme?

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.

+4  A: 

Mutation, though allowed, is strongly discouraged in Scheme. PLT even went so far as to remove set-car! and set-cdr! (though they "replaced" them with set-mcar! and set-mcdr!). However, a spec for append! appeared in SRFI-1. This append! 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:

(define (my-append! a b)
  (if (null? (cdr a))
      (set-cdr! a b)
      (my-append! (cdr a) b)))

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) as b. The reason a must be at least length 1 is because you can't set-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 last cons pair, which is (<last element> . null). So we first see if a is already the last element in the list by checking for null in the cdr. If it is, we use set-cdr! to set it to the list we're appending, and we're done. If not, we have to call my-append! on the cdr of a. Each time we do this we get closer to the end of a. 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.

Kyle Cronin
Follow up question - if mutation is strongly discouraged then what is best practices for a pattern like "building a list from a wizard dialog" ?Coming from a Ruby background, this is something that I've been struggling with understanding..the mindset moreso than just the actual code to solve the problem.
Cuervo's Laugh
In functional programming, whenever you want to change a data structure or somesuch, you pass it into a function and get back the modified data structure as the return. However the 'original' structure is left intact and what you're given is a new, altered copy (so to speak).
Kyle Cronin
There is no "full guarantee" possible, which is exactly the problem with destructive functions -- if the list was empty, then no version of `append!` will ever be able to modify it.
Eli Barzilay
I thought only empty list literals `'()` are immutable, but when they are constructed as in `(list)` then they are mutable.
notnoop