5

我不明白两者之间的区别是什么(对于人为的例子感到抱歉):

(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 个局部变量,它们相互引用并引用自身,并评估并返回一个主体。

我在这里遗漏了什么,还是与 scoped sletrec同义?define

我知道这可能取决于实现;我试图了解 Lisps 的基础知识。

4

2 回答 2

8

您是正确的,您的代码的版本define和版本之间存在相似之处。letrec然而,魔鬼在细节中。在 R5RS 中,内部defineletrec语义。在 R6RS 中,内部defineletrec*语义

有什么不同?您的代码实际上只是突出了这种差异。正如哲豪的回答所提到的,您对totalandcount内部的定义letreclengthandsum是不正确的:length并且sum不能保证在您评估(length elems)and的值时受到约束(sum elems),因为不能保证这些变量的绑定是左-向右。

letrec*类似于letrec,但具有从左到右的保证。因此,如果您将其更改letrecletrec*,就可以了。

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

于 2012-07-13T02:51:36.783 回答
4

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

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

使用本地范围定义的相同代码将是

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

这是一个人为的例子,但通常使用 let 宏进行本地绑定被认为更实用。

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

于 2012-07-13T02:31:21.643 回答