【问题标题】:set! and aliasing in Scheme放!和 Scheme 中的别名
【发布时间】:2018-05-19 17:44:41
【问题描述】:

我无法理解方案中似乎不一致的破坏性操作。即为什么 bar 在下面的例子中没有改变

(define foo '(a b))
(define bar foo)
(set! foo '(c d))
foo
>(c d)
bar
>(a b)

但是,这会通过更改 foo 来更改条形

(define foo '(a b))
(define bar foo)
(set-car! foo 'c)
foo
>(c b)
bar
>(c b)

如果我理解正确的话,bar 在这两个例子中都指向它的汽车中的 foo,但不知何故旧的 foo 在使用 set 时不会改变!在第一个。我在任何地方都找不到关于如何以及为什么会发生这种情况的答案。

【问题讨论】:

    标签: scheme


    【解决方案1】:

    别名并不意味着bar 指向foo,而是bar 指向与foo 指向相同的事物时间>。如果图片有帮助,这里有关于正在发生的事情的方框图和箭头图。在你的设置代码之后,你有这个:

    (define foo (list 'a 'b))
    (define bar foo)
    

    现在,如果您运行第一次尝试,您将创建一个新列表并将 foo 设置为指向它,而不更改任何关于 bar 引用的内容。

    (set! foo (list 'c 'd))
    

    但是,如果您改为运行此代码,则会修改 foobar 目前恰好指向的(共享)列表。

    (set-car! foo 'c)
    

    【讨论】:

    • IOW 在 Scheme 中没有别名; (define name value)name 创建一个绑定,并将其设置为指向表达式value,而不是变量(即@987654342 的绑定@) 本身。因为它可能是一个成熟的表达式,比如(+ 1 value)
    • 别名是指两个符号名称引用内存中的相同数据。指向同一个列表的变量 'foo' 和 'bar' 是别名的一个例子。执行 '(set! foo ...)' 更改了 'foo' 的引用,它不会更改别名数据结构。我认为您所说的“别名”以及 OP 期望 'bar' 是对 'foo' 变量本身的间接引用。不,我认为 Scheme 没有提供任何方法来做到这一点。
    • 是的,我就是这个意思;就像 C++ 中的引用一样。
    • 通过盒子很容易模拟。 (begin (define foo 10) (define-sticky bar foo) (set! foo 20) bar) ; ==> 20 == (begin (define foo (box 10)) (define bar foo) (set-box! foo 20) (unbox bar))。因此它不存在的原因不是它难以实现。
    • @Sylwester can't find docs on define-sticky。改变一个盒子仍然只是改变一个结构(“盒子就像一个单元素向量,通常用作最小可变存储。”)。
    【解决方案2】:

    define 生成一个变量。计算对象位置的名称。

    (define foo (list 'a 'b))
    

    这里的foo 可能指向地址240。在评估foo 时,您会得到240,因此(define bar foo) 将创建第二个变量,该变量指向内存中的同一对象。地址未显示在 REPL 中,因此您将看到 (a b),因为 REPL 会打印地址 240 中的内容。

    内存中的这个对象是一个cons,所以每一个都只是指向内存中其他值的两个指针。因此,当你这样做时:

    (set-car foo 'c)
    

    您正在通过将car 指针从a 的地址表示更改为c 的地址表示来更改位于地址240 的缺点。由于foobar 都指向地址240 的同一个cons 单元格,因此它们都将获得更改,因为它们指向相同的数据

    如果您使用set!,您只会更改名称指向的值:

    (set! foo (cons 'd (cdr foo)))
    

    现在这会将变量指针从指向240 更新到其他地址。因此,您可以评估 foobar 并查看它们是不同的值:

    foo ; ==> (d b)
    bar ; ==> (c b)
    

    这里foo 可能位于地址256。由于我们在 foo 中使用了旧值 cdr,我们知道它们的尾部是相同的:

    (cdr foo)                 ; ==> (b)
    (eq? (cdr foo) (cdr bar)) ; ==> #t
    

    如果您将set! bar 设置为不同的值:

    (set! bar foo)
    

    现在foobar 都指向256,如果不再使用240,则该位置的对可能会被垃圾收集,但由于原始foocdrcdr,假设地址248,该地址不会被垃圾回收,因为它是由地址256 中的cons 指向的。

    注意:我已经更改了 foo 的定义,因为您不允许使用 set-car'(a b) 这样的文字,因为它们是不可变的。 (list 'a 'b) 每次执行该行代码时都会创建一个新列表,您可以确保该地址不会在例如。 '(d a b).

    NB2:Scheme 报告尽量避免规定如何存储值。因此他们没有在报告中使用概念地址。 Scheme 和 CL 的大多数实现都对它们的数据进行了非常相似的建模。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-22
      • 2020-02-06
      • 2022-01-06
      • 2020-07-24
      • 2013-02-06
      • 2013-06-24
      • 1970-01-01
      相关资源
      最近更新 更多