定义,设置和设置之间的区别!

时间:2011-03-23 13:37:46

标签: scheme

好的,这是一个相当基本的问题:我关注的是SICP视频,我对defineletset!之间的差异感到有些困惑。

1)根据视频中的Sussman,允许define仅将值附加到可变一次(除非在REPL中),特别是不允许在线定义两个。然而,Guile愉快地运行了这段代码

(define a 1)
(define a 2)
(write a)

和输出2,如预期的那样。事情有点复杂,因为如果我尝试这样做(编辑:在上述定义之后)

(define a (1+ a))

我收到错误,而

(set! a (1+ a))

是允许的。我仍然不认为这是set!define之间的唯一区别:我错过了什么?

2)definelet之间的差异让我更加困惑。我在理论上知道let用于绑定局部范围内的变量。尽管如此,在我看来,这与define的工作方式相同,例如我可以替换

(define (f x)
    (let ((a 1))
        (+ a x)))

(define (g x)
    (define a 1)
    (+ a x))

f以及g的工作方式相同:特别是变量a也在g之外未绑定。

我能看到这个有用的唯一方法是let可能具有整个函数定义的更短范围。在我看来,总是可以添加一个匿名函数来创建必要的范围,并立即调用它,就像在javascript中一样。那么,let的真正优势是什么?

4 个答案:

答案 0 :(得分:32)

你的困惑是合理的:'let'和'define'都创建了新的绑定。 “让”的一个优点是它的含义非常明确;各种Scheme系统(包括Racket)之间绝对没有关于普通的'let'意味着什么的分歧。

'定义'形式是一个不同的鱼。与'let'不同,它不会用括号围绕主体(绑定有效的区域)。此外,它可能意味着顶层和内部的不同事物。不同的Scheme系统对'define'有着截然不同的含义。实际上,Racket最近通过添加可以发生的新上下文来改变'define'的含义。

另一方面,人们喜欢'定义';它具有较少的缩进,并且它通常具有“do-what-what-mean-mean”级别的范围,允许递归和相互递归过程的自然定义。事实上,前几天我被这种情况所困扰:)。

最后,'设置!';比如'let','set!'非常简单:它改变了现有的绑定。

FWIW,了解DrRacket中这些范围的一种方法(如果您正在使用它)是使用“检查语法”按钮,然后将鼠标悬停在各种标识符上以查看它们的绑定位置。

答案 1 :(得分:16)

您的意思是(+ 1 a)而不是(1+ a)吗?后者在语法上没有效力。

let定义的变量范围与后者绑定,因此

(define (f x)
  (let ((a 1))
    (+ a x)))

在语法上是可行的,而

(define (f x)
  (let ((a 1)))
  (+ a x))

不是。

所有变量必须在函数的开头define d,因此可以使用以下代码:

(define (g x)
  (define a 1)
  (+ a x))

虽然此代码会生成错误:

(define (g x)
  (define a 1)
  (display (+ a x))
  (define b 2)
  (+ a x))

因为定义之后的第一个表达式意味着没有其他定义。

set!未定义变量,而是用于为变量分配新值。因此,这些定义毫无意义:

(define (f x)
  (set! ((a 1))
    (+ a x)))

(define (g x)
  (set! a 1)
  (+ a x))

set!的有效用途如下:

(define x 12)
> (set! x (add1 x))
> x
13

虽然不鼓励,因为Scheme是一种功能语言。

答案 2 :(得分:7)

John Clements的回答很好。在某些情况下,您可以看到每个版本的Scheme中define成为什么,这可能有助于您了解正在发生的事情。

例如,在 Chez Scheme 8.0 (它有自己的define怪癖,特别是W6 R6RS!):

> (expand '(define (g x)
             (define a 1)
             (+ a x)))
(begin
  (set! g (lambda (x) (letrec* ([a 1]) (#2%+ a x))))
  (#2%void))

您会看到“顶级”定义变为set!(尽管在某些情况下仅展开define会改变一些事情!),但内部定义(即{{1}在另一个块内)成为define。不同的方案会将这种表达扩展到不同的东西。

MzScheme v4.2.4

letrec*

答案 3 :(得分:2)

您可以多次使用define,但事实并非如此 惯用语:define表示您要为其添加定义 环境和set!意味着你正在改变一些变量。

我不确定Guile以及为什么它会允许(set! a (+1 a))但是 如果a尚未定义但不起作用。通常人们会使用 define引入一个新变量并仅使用set!进行变异 后面。

您可以使用匿名函数应用程序而不是let 事实上,这通常正是let扩展到的,几乎就是这样 总是一个宏。这些是等价的:

(let ((a 1) (b 2))
  (+ a b))

((lambda (a b)
   (+ a b))
 1 2)

您使用let的原因是它更清楚:变量名称就在值的旁边。

在内部定义的情况下,我不确定Yasir是什么 正确。至少在我的机器上,在R5RS模式下运行Racket 常规模式允许内部定义出现在中间 功能定义,但我不确定标准是什么。在任何 案例,很久以后在SICP中,内部定义的诡计就是 深入讨论。在第4章中,如何实现相互递归 探讨了内部定义及其对实现的意义 元认知翻译。

坚持下去! SICP是一本精彩的书,视频讲座很精彩。