Scheme中let和let *有什么区别?

时间:2013-09-18 07:18:11

标签: lisp scheme gimp

我正在为GIMP编写脚本并使用let*,因为它是我拍摄的样本。但它似乎只是一个与let完全相同的lambda糖。他们为什么不同?他们之间有什么区别?

3 个答案:

答案 0 :(得分:9)

它们在变量绑定的顺序上有所不同。以此为例:

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

此代码将失败,因为b需要a,之前尚未定义。它在同一个let中定义,但Scheme会将您的所有let定义仅作为一个语句,并且不允许它们相互引用。在Gambit计划中,它提出了:

*** ERROR IN ##raise-unbound-global-exception -- Unbound variable: a

相反,let*将绑定let的第一个变量,然后绑定第二个变量......等等:

> (let* ((a 1)(b (+ a 2))) b)
3

按预期工作。

感兴趣的第三种形式是letrec,它不仅允许let中的变量引用其他变量,而且还让它们自己引用(例如,用于递归)。这使您可以编写如下代码:

> (letrec ((f (lambda(n) ;; Takes the binary log2 recursively
               (cond
                ((= n 1) 0)
                (else (+ 1 (f (/ n 2))))))))
   (f 256)) ;; 2^8 = 256
8

如果您尝试使用letlet*定义递归函数,它会告诉您变量未绑定。

所有这些都可以通过巧妙的重新排列/嵌套let语句来实现,但let*letrec在某些情况下可以更方便和可读。

答案 1 :(得分:4)

它们绑定变量的方式不同。一个let中的所有变量使用相同的lambda形式,因此您可以这样做:

(let ((x 10) (y 20))
  (let ((x y) (y x))
     (display (list x y)))) ; prints (20 10)

使用let切换内部let*时,您会发现第二个绑定到第一个绑定中绑定的内容而不是let*之前的内容

(let ((x 10) (y 20))
  (let* ((x y) (y x))
     (display (list x y)))) ; prints (20 20)

原因是

(let* ((x y) (y x))
   ...)

相同
(let ((x y))
  (let ((y x))
    ...))

答案 2 :(得分:2)

let和let *用于绑定变量,两者都是语法糖(宏),但让*一个接一个地绑定变量(从左到右,或从上到下)。差异也在于不同的范围。让每个变量的范围只是表达式,而不是绑定。在let *中,每个变量的范围是表达式和​​之前的绑定。 让*你做这样的事情(b a)

...
(let* ((a 1)
       (b a))
   ...)
...

让你不要。

实施let:

(define-syntax let
 (syntax-rules ()
  ((_ (( variable value ) ...) body ...)
   (( lambda (variable ...) body ...) value ...))))

let *:

的实现
(define-syntax let*
  (syntax-rules ()
    ; pattern for one binding
    ((_ ((variable value)) body ...)
      ((lambda (variable) body ...) value))
    ; pattern for two or more bindings
    ((_ ((variable value) . other) body ...)
      ((lambda (variable) (let* other body ...)) value))))