为什么首选在Scheme中定义?

Ell*_*tus 5 scheme coding-style let

我总是写这样的Scheme程序(并看到它们写的)像这样:

(define (foo x)
  (let ((a ...))
       ((b ...))
    ...))
Run Code Online (Sandbox Code Playgroud)

我的一个学生写道:

(define (foo x)
  (define a ...)
  (define b ...)
  ...)
Run Code Online (Sandbox Code Playgroud)

两者都给出相同的结果.我理解行为上的差异:第一个创建一个指向过程应用程序框架的新框架,而后者直接修改过程应用程序框架.后者会产生稍好的性能.

另一个区别是前者避免begin在过程体中的一系列指令之前使用隐式.

为什么以前的标准风格?

Chr*_*ung 5

实际上,两种风格都很好.事实上,有些人更喜欢使用内部定义.

此外,后者也不一定"直接修改程序应用程序框架"; 内部定义的处理方式与letrec(对于符合R5RS的系统)或letrec*(对于符合R6RS和R7RS的系统)相同.因此,您的第二个示例与以下内容完全相同:

(define (foo x)
  (letrec* ((a ...)
            (b ...))
    ...))
Run Code Online (Sandbox Code Playgroud)

实际上,使用一个例子,球拍重写的内部定义为等效的letrec*表达式和有因此性能没有差异(超出任何差之间存在letletrec*,当然).


Syl*_*ter 5

这并不完全等同。define在过程主体中更像是 a,letrec因此您可能会感到惊讶,define在所有这些都完成并且要执行过程主体之前,您无法使用 a 中的值绑定。想象一下你想做 x + y * z:

(define (test x y z)
  (let ((ytimesz (* y z)))
     (let ((sum (+ x ytimesz)))
       (dosomething sum))))
Run Code Online (Sandbox Code Playgroud)

您在此处使用嵌套 let 的原因是因为ytimesz不能在创建它的同一个 let 中访问。我们有另一种特殊形式let*

(define (test x y z)
  (let* ((ytimesz (* y z)) (sum (+ x ytimesz)))
       (dosomething sum)))
Run Code Online (Sandbox Code Playgroud)

letrecandletrec*是相似的,但允许递归,因此在 lambda 中,您可以调用其他绑定成员之一或自身。现在,根据您使用的 Scheme 版本,您将在编写时获得其中之一:

(define (test x y z)
  (define ytimesz (* y z))
  (define answer (+ x ytimesz)) ;might work, might not
  (dosomething answer))
Run Code Online (Sandbox Code Playgroud)

#!R7RS#!R6RS#!Racket这是完全确定,因为这被定义为letrec*

#!R5RS但是,在中,它根本不起作用。重写是这样完成的letrec,它将所有变量(ytimeszanswer)初始化为一个未定义的值,然后将表达式的评估分配给临时变量,然后set!将变量赋值为临时值的值,以确保对它们中的任何一个的所有使用都结束作为未定义的值和一些甚至信号错误(Racket 在 R5RS 模式中确实如此。对于在调用时评估主体中的绑定的 lambda 表达式,这没问题,它是为这些letrec和内部define最初的目的。

define用来存储简单的值和过程。第二个我认为我需要使用一个预先计算的值,我可能会将整个内容重写为 a let*or combinedefine和一个简单的let.

  • R6RS 内部定义,像 R7RS 一样,使用 `letrec*`,而不是 `letrec`。 (2认同)