【问题标题】:Why doesn't the set! function modify the original list in Scheme (r5rs)?为什么没有集!函数修改 Scheme (r5rs) 中的原始列表?
【发布时间】:2014-05-11 17:56:29
【问题描述】:

我正在尝试编写一个函数,它接受一个列表 (x) 和一个数字 (y),并删除列表中该数字的每次出现。前任。 (deepdeleting '(0 0 1 2 0 3 0) 0) ===> '(1 2 3) 到目前为止,这是我所拥有的:

 (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
       )
    )

代码有效,但我的问题是我希望它修改原始列表,而不仅仅是返回一个新列表。现在是这样的:

> (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)

这对我来说似乎很奇怪,因为这两个设定车!和设置-cdr!功能似乎改变了输入列表,而设置!不...

任何见解将不胜感激!

【问题讨论】:

  • 你永远不会修改list,只修改变量x。此外,一些 Scheme 不允许文字列表的变异。
  • 好吧,那有什么区别可以让set-car!然后更改列表?
  • set-car!set-cdr! 改变了这对。 set! 更改最里面的绑定。到列表的绑定指向第一对,procedur 参数是它自己的绑定。 IE。您正在更改本地 x 并且从不列出。
  • 所以,为了确保我理解......以 x 开头,并且 list 绑定到同一个文字列表,但是当我 !set x 我将它绑定到一个新列表,它有不影响list的绑定?
  • 我仍然对固定车感到困惑!和设置-cdr!虽然部分...你是说对是可变的,而列表不是?

标签: functional-programming scheme r5rs


【解决方案1】:

当您使用set! 时,您正在重新定义最里面的绑定:

(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

变量绑定与列表结构突变完全不同。这里使用绑定来指向已更改的对:

(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 ...)

因此,您可以使用 set-cdr!set-car! 对对进行变异,与原始列表的绑定将指向第一对。因此,您需要结果以与第一个相同的对开始。有了它,您可以通过这种方式进行变异过程:

#!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

虽然lst 在删除期间绑定到列表,并且同一个列表少了一个元素,但在删除之外没有绑定到它!过程,因此结果将永远丢失。

编辑 对于 R5RS,删除前两行并添加 error:

;; won't halt the program but displays the error message
(define (error sym str)
  (display str)
  (newline))

【讨论】:

  • 谢谢,您的解释帮助很大。我正在研究 r5rs,所以我不能使用您在上一个示例中提供的某些关键字,但您已经为我清除了一些东西!
  • @Kittenmittons 我已将 when 更改为 if。通过文章末尾的注释,您将能够在 R5RS 下运行它。
猜你喜欢
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 2015-07-30
  • 1970-01-01
  • 2019-07-27
  • 1970-01-01
  • 2011-07-28
  • 1970-01-01
相关资源
最近更新 更多