Bug?

From: Harvey J. Stein <abel_at_netvision.net.il>
Date: Thu, 7 Nov 1996 12:46:38 +0200

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

This archive was generated by hypermail 2.3.0 : Mon Jul 21 2014 - 19:38:59 CEST