David Fox writes:
> Using snow 3.1 on my Linux box the following function produces
> different results each time I run it on (split1 (string->list
> "ab/cd"))
>
> (define (split1 lst)
> (cond ((null? lst) '(()))
> ((eq? (car lst) #\/) (cons () (split1 (cdr lst))))
> (else
> (let ((sf (split1 (cdr lst))))
> (set-car! sf (cons (car lst) (car sf)))
> sf))))
>
> If I replace "'(())" with "(list (list))" it works correctly.
It's not a bug, really.
The problem is that you've written some self-modifying code. The
culprit is the set-car! When (split1 ()) returns, it returns '(())).
Then the caller, which would be (split1 '(#\d)) lets sf be this
result, and then does (set-car! sf (cons (car '(#\d))) (car sf)),
since sf is '(()), this modifies '(()). Since '(()) is just sitting in
memory, the next time you run split1, the (split1 '()) call returns a
different result.
Using (list (list)) fixes it, because it creates a new copy of '(())
each time it runs.
Technically, '(()) should be in read-only memory, and the above
set-car! should throw an error, but all scheme implementations I've
tested don't do this.
Here's a simple example of similar self modifying code to illustrate
the point.
(define (save-add)
(let ((x '(a . 1)))
(set-cdr! x (1+ (cdr x)))
x))
STk> (save-add)
(a . 2)
STk> (save-add)
(a . 3)
You'd think that each time save-add is called, it sets x to
'(a . 1), increments (cdr x), and returns x, so it should always
return '(a . 2). However, since '(a . 1) isn't recreated each time
save-add is executed, the 2nd time around, '(a . 1) has become '(a
. 2), so the function returns '(a . 3).
Not to belabor the point, but since I find my own above description
unclear, maybe it'd be helpful to describe it differently:
The above let gets converted to a lambda expression, so that the above
definition is equivalent to:
(define (save-add)
((lambda (x) (set-cdr! x (1+ (cdr x))) x)
'(a . 1)))
Now since '(a . 1) is a quoted list, it's just created when save-add
is defined, and each time save-add is executed, we're modifying the
cdr of '(a . 1). Thus, the 2nd call to save-add returns '(a . 3)
instead of '(a . 2).
Of course, if one wrote (cons 'a 1) instead, then you'd be applying
the above lambda to a new cons cell each time save-add is executed,
in which case (save-add) would always return '(a . 2).
--
Dr. Harvey J. Stein
Berger Financial Research
abel_at_netvision.net.il
Received on Thu Nov 07 1996 - 11:48:50 CET