用于创建本地绑定的'let'或'letrec'和'define'之间有什么区别?

时间:2012-07-13 02:15:17

标签: variables scope scheme letrec

我不明白它们之间的区别(对于人为的例子而言):

(define average
  (lambda (elems)

   (define length
     (lambda (xs)
      (if (null? xs)
          0
          (+ 1 (length (cdr xs))))))

   (define sum
     (lambda (xs)
      (if (null? xs)
          0
          (+ (car xs) (sum (cdr xs))))))

   (define total (sum elems))

   (define count (length elems))

   (/ total count)))

(define average
  (lambda (elems)
   (letrec ((length
              (lambda (xs)
               (if (null? xs)
                   0
                   (+ 1 (length (cdr xs))))))

            (sum
              (lambda (xs)
               (if (null? xs)
                   0
                   (+ (car xs) (sum (cdr xs))))))

            (total (sum elems))

            (count (length elems)))

     (/ total count))))

据我所知,他们都创建了一个新范围,并在该范围内创建了4个局部变量,这些变量相互引用并与自身相关,并评估并返回一个正文。

我在这里遗漏了什么,或letrec是否与范围define同义?

我知道这可能是依赖于实现的;我试图了解Lisps的基本原理。

2 个答案:

答案 0 :(得分:6)

您的代码的defineletrec版本之间存在相似之处。然而,魔鬼在细节中。在R5RS中,internal define has letrec semantics。在R6RS中,internal define has letrec* semantics

有什么区别?您的代码实际上只是强调了这种差异。正如哲浩的回答所提到的那样,totalcount在与letreclength相同的sum内的定义不正确:length和{ {1}}不能保证在您评估sum(length elems)的值时受到约束,因为这些变量的绑定不能保证从左到右。< / p>

(sum elems)letrec*类似,但具有从左到右的保证。因此,如果您将letrec更改为letrec,那就没关系。

现在,回到我最初的评论:因为R5RS的内部letrec*使用define语义,即使您的letrec版本的代码在R5RS实现下也是不正确的,但它会是好吧,在R6RS实现下,它具有define语义。

答案 1 :(得分:4)

let / letrec和define都将创建本地范围的定义。但是,当你不在内部和隐式begin语句时,让/ letrec更方便。例如,以下代码使用let宏。

(define (test) (let ((x 1)) x))

使用本地作用域定义的相同代码将是

(define test (lambda () (define x 1) x))

这是一个人为的例子,但通常使用let宏来进行局部绑定被认为更有用。

此外,您的示例代码未正确使用letrec。你不需要letrec声明中的定义(实际上它们确实不应该存在)。