0

I am trying to write a function which takes a list (x) and a number (y) and deletes every occurance of that number in the list. Ex. (deepdeleting '(0 0 1 2 0 3 0) 0) ===> '(1 2 3) Here's what I have so far:

 (define (deepdeleting x y)
      (if (pair? x)
          (if (eqv? (car x) y)
              (begin
                (set! x (cdr x))
                (deepdeleting x y)
               )
               (deepdeleting (cdr x) y) ; else
           )
          x ; else
       )
    )

The code works, but my problem is I want it to modify the original list, not just return a new list. Right now this is what happens:

> (define list '(0 0 1 2 0 3 0))
> (deepdeleting list 0)
(1 2 3)
> list
(0 0 1 2 0 3 0) ; <<< I want this to be (1 2 3)

This seems strange to me since both the set-car! and set-cdr! functions seem to change the input list, whereas set! does not...

Any insight would be much appreciated!

Kittenmittons
  • 400
  • 3
  • 14
  • 1
    You are never modifying `list` only the variable `x`. Also, some Scheme's will not allow mutation of a literal list. – leppie Apr 01 '14 at 05:52
  • Ok, so what's the difference that allows set-car! to change the list then? – Kittenmittons Apr 01 '14 at 06:10
  • `set-car!` and `set-cdr!` alters the pair. `set!` alters the innermost binding. A binding to a list points the the first pair and a procedur argument is it's own binding. Ie. you are changing the local x and never list. – Sylwester Apr 01 '14 at 06:10
  • So, to make sure I understand... to begin with x and list are bound to the same literal list, but when I !set x I'm binding it to a new list, which has no affect on the binding of list? – Kittenmittons Apr 01 '14 at 06:30
  • I'm still sort of confused by the set-car! and set-cdr! part though...are you saying pairs are mutable while lists are not? – Kittenmittons Apr 01 '14 at 06:31
  • @Kittenmittons Out of curiousity, have you used any other programming languages (e.g., C, C++, Java)? In those languages, you can define a function `void foo(int x) { x = 7; }` and then in some other code `int a = 3; foo(a); /* a is 3 here */`, the value of `a` is still `3` afterward. `x` is a variable that's _local_ to `foo`. The same thing is happening here. – Joshua Taylor Apr 01 '14 at 18:19

1 Answers1

2

When you use set! you are redefining the innermost binding:

(define test 10)

(set! test 11) ; changes global test to 11

(define (change-test value)
  (set! test value))

(change-test 12) ; changes global test to 12

(define (change-test! value new-value)
  (display value)
  (set! value new-value) ; changes the local binding value
  (display value))

(change-test! test 13) ; changes nothing outside of change-test, prints 12 then 13

Variable bindings are totally different than list structure mutation. Here a binding is used to point to a pair that is altered:

(define lst '(1 2 3))
(define lst2 (cdr lst)) ; lst2 shares structure with lst

(set-cdr! lst2 '(8 7 6 5))
lst2 ; ==> (2 8 7 6 5)
lst  ; ==> (1 2 8 7 6 5) the original binding share structure thus is changed too

(set-cdr! lst lst)  ; makes a circular never ending list (1 1 1 1 ...)
(eq? lst (cdr lst)) ;==> #t

(set-car! lst 2)    ; changes lst to be never ending list (2 2 2 2 ...)

So you can mutate pairs with set-cdr! and set-car! and a binding to the original list will point to the first pair. Thus you need the result to start with the same pair as the first. With that you can make your mutating procedure this way:

#!r6rs 
(import (rnrs) (rnrs mutable-pairs))

(define (remove! lst e)
  (if (pair? lst)
    (let loop ((prev lst)(cur (cdr lst)))
      (if (pair? cur)
          (if (eqv? (car cur) e)
              (begin
                (set-cdr! prev (cdr cur))
                (loop prev (cdr cur)))
              (loop cur (cdr cur)))
          (if (eqv? (car lst) e)
            (if (pair? (cdr lst))
                (begin
                  (set-car! lst (cadr lst))
                  (set-cdr! lst (cddr lst)))
                (error 'first-pair-error "Not possible to remove the first pair"))
            #f)))
    #f))
(define test '(0 0 1 2 0 3 0))
(define test2 (cdr test))
test2 ;==> (0 1 2 0 3 0)
(remove! test 0)
test  ; ==> (1 2 3)
test2 ; ==> (0 1 2 0 3 0)
(remove! '(0) 0)
; ==> first-pair-error: Not possible to remove the first pair

(remove! '(1 2 3) 2) ; this works too but you have no way of checking

While lst is bound to the list during removal and the same list has one element less there was not binding to it outside of the remove! procedure so the result is forever lost.

EDIT For R5RS remove the first two lines and add error:

;; won't halt the program but displays the error message
(define (error sym str)
  (display str)
  (newline))
Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • Thanks, your explanation helped a lot. I'm working on r5rs so I can't use certain keywords you provided in the last example but you've cleared some things up for me! – Kittenmittons Apr 01 '14 at 07:07
  • @Kittenmittons I've changes `when` to `if`. With the notes in the end of the post you'll be able to run it under R5RS. – Sylwester Apr 01 '14 at 07:18