26

在方案中使用set!赋值运算符有什么意义?为什么不只是rebind使用一个新值的变量define

> (define x 100)
> (define (value-of-x) x) ;; value-of-x closes over "x"
> x
100
> (value-of-x)
100
> (set! x (+ x 1))
> x
101
> (value-of-x)
101
> (define x (+ x 1))
> x
102
> (value-of-x)
102
> 
4

4 回答 4

43

虽然defineset!都会在相同的范围内重新定义一个值,但当范围不同时,它们会做两件不同的事情。这是一个例子:

(define x 3)

(define (foo)
  (define x 4)
  x)

(define (bar)
  (set! x 4)
  x)

(foo) ; returns 4
x     ; still 3
(bar) ; returns 4
x     ; is now 4

正如你所看到的,当我们创建一个新的词法作用域时(例如当我们创建define一个函数时),在该作用域内定义的任何名称都会掩盖出现在封闭作用域中的名称。这意味着当我们defined xto 4in 时foo,我们确实为旧值创建了一个新x值。在bar, 因为foo在该范围内不存在,所以set!查找封闭范围以查找并更改 的值x

另外,正如其他人所说,您只应该define在范围内使用一次名称。有些实现会让你摆脱多个defines,而有些则不会。此外,您只应该set!在已经defined 的变量上使用。同样,执行此规则的严格程度取决于实施情况。

于 2009-02-08T20:50:13.383 回答
7

通常不允许define一个变量超过一次。大多数 REPL 允许在您尝试时方便,但如果您尝试在 Scheme 程序中这样做,它会给您一个错误。

例如,在 mzscheme 中,程序

#lang scheme
(define x 1)
(define x 2)

给出错误

test.ss:3:8: module: duplicate definition for identifier at: x in: (define-values (x) 2)

此外,define在其他上下文中使用时具有不同的含义。该程序

#lang scheme
(define x 1)
x
(let ()
  (define x 2)
  x)
x

有输出

1
2
1

这是因为define某些结构中的 s 实际上被视为letrecs。

于 2009-02-08T20:41:39.880 回答
3

当您使用词法绑定时,您不会 define

(let ((x 1))
  (set! x (+ x 1))
  x)
于 2009-02-08T17:50:49.383 回答
3

当您使用定义时,您使用新值创建一个新变量,而旧变量仍然存在旧值;它只是被新的隐藏了。在命令行上,您看不到 set! 的区别,但 define 将不能用于例如命令式程序中的循环计数器。

于 2009-02-08T18:40:44.743 回答