为什么不按照词汇上封闭的`define`来实现`let`? [英] Why not implement `let` in terms of lexically closed `define`?

查看:80
本文介绍了为什么不按照词汇上封闭的`define`来实现`let`?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用了Lisp家族语言几年了,觉得我对它们掌握得很好.我现在正在编写自己的Lisp(当然是时尚),但是几乎完全避免了重新实现Scheme,Common Lisp和朋友使用的相同模式.我总是觉得奇怪的一件事是let的所有变体(letrecfletlabelslet* ...).

I have been working with lisp-family languages for several years and feel like I have a pretty good grasp on them. I'm now writing my own lisp (as is the fashion, of course), but almost entirely avoiding re-implementing the same patterns that Scheme, Common Lisp and friends have used. One particular thing that I always found odd was all the variants of let (letrec, flet, labels, let*...).

假设一个Lisp的没有过去遗留的遗留"实现,我想只能够编写如下内容:

Say in a "no legacy carried over from the past" implementation of a lisp, I'd like to just be able to write things like:

(let ((x 7)
      (y 8)
      (z (* x y)))
  (str "x * y * z = " (* x y z)))

同样,我希望能够写:

(let ((odd? (lambda (n) (if (zero? n) false (even? (dec n)))))
      (even? (lambda (n) (if (zero? n) true (odd? (dec n)))))
  (odd? 7))

仅通过在lambda主体内部使用define,我就可以非常有效地实现这两个变体.请原谅clojure-scheme-hybrid代码:

I could implement both of those variants quite efficiently by just using define inside the body of a lambda. Excuse the clojure-scheme-hybrid code:

(defmacro let (bindings & body)
  `((lambda ()
      ,@(map (lambda (name-value) (cons 'define name-value)) bindings)
      ,@body)))

因为这仅在lambda主体的封闭环境中引入了绑定,所以相互递归可以起作用,并且变量可以取决于先前定义的存在.同样,由于lambda会关闭创建它的环境,因此扩展(define x expression) (define y expression)会按预期工作.

Because this just introduces the bindings within the closed environment in the body of the lambda, mutual recursion can work and variables can depend on the existence of previous definitions. Similarly, because a lambda closes the environment in which it is created, expanding the (define x expression) (define y expression) works as expected.

那么,为什么要通过使用许多显式形式来使事情复杂化呢?我在俯视什么吗?如上所示,我已经实现了自己的let,它似乎在所有情况下都能按预期工作.这也减少了嵌套函数应用程序的成本开销,例如let*.

So why complicate matters by having numerous explicit forms? Am I overlooking something? I have implement my own let as I demonstrate above and it seems to work in all cases exactly as expected. This also reduces the cost overhead of nested function applications, such as in the case of let*.

推荐答案

R5RS使用letrec定义内部定义(har har),R6RS和R7RS使用letrec*定义内部定义.您描述的行为正是letrec*的含义.

Internal definitions are defined (har har) by R5RS to use letrec, and by R6RS and R7RS to use letrec*. The behaviour you're describing is exactly what letrec* is.

但是,在某些情况下,您想使用外部绑定,而又不想让内部绑定在定义期间遮盖它们.在这种情况下,letlet*letrecletrec*更合适.

However, there are cases where you want to use the outer bindings, and you don't want the inner bindings to shadow them during their definitions. In this case, let and let* are more appropriate than letrec and letrec*.

这是什么意思?这是let提供letrec不提供的外部范围的一种方式:

What do I mean by this? Here's one way that let provides outer scoping that letrec doesn't:

(let ((x 1)
      (y 2))
  (let ((x (+ x y))
        (y (- x y)))
    (format #t "x = ~a, y = ~a~%" x y)))

在这里,在(+ x y)(- x y)表达式中,我们使用的是>和y outer 绑定.因此,内部x将为3,内部y将为-1.

Here, in the (+ x y) and (- x y) expressions, we are using the outer bindings of x and y. Thus the inner x will be 3, and the inner y will be -1.

使用let*相似,除了绑定是顺序的:

Using let* is similar except that the bindings are sequential:

(let ((x 1)
      (y 2))
  (let* ((x (+ x y))
         (y (- x y)))
    (format #t "x = ~a, y = ~a~%" x y)))

此处,内部x的计算方式与let情况相同,但是内部y的定义将使用内部x而不是外部x(但仍使用外部y,因为尚未绑定内部y).因此,内部x将为3,内部y将为1.

Here, the inner x is evaluated the same as for the let case, but the inner y's definition will use the inner x instead of the outer x (but it still uses the outer y, as the inner y hasn't been bound yet). Thus the inner x will be 3, and the inner y will be 1.

使用letrecletrec*时,所有外部绑定将被相同名称的内部绑定遮盖,在这种情况下,您将无权访问外部xy.此属性使它们可以用于自引用函数和/或数据结构.

When using letrec and letrec*, all the outer bindings will be shadowed by the inner bindings of the same name, and you do not have access to the outer x or y in those cases. This property is what allows their use for self-referential functions and/or data structures.

letrecletrec*相似,除了对于letrec而言,所有值都首先被求值,然后同时最后绑定到变量.而对于letrec*,将评估每个变量的值并按从左到右的顺序进行绑定.

letrec and letrec* are similar, except that for letrec, the values are all evaluated first, then bound to the variables at the end, simultaneously; whereas for letrec*, the values for each variable are evaluated and bound sequentially from left to right.

为了演示四种let类型,我编写了一个小小的Racket宏,该宏可让您测试每种类型的行为:

To demonstrate the four let types, I wrote a little Racket macro that allows you to test the behaviour of each:

#lang racket
(define-syntax test-let
  (syntax-rules ()
    ((_ let)
     (let ((x "outer x")
           (y "outer y")
           (p (lambda (x y label)
                (printf "~a: x = ~s, y = ~s~%" label x y))))
       (let ((before (p x y "before"))
             (x (begin
                  (p x y "during x")
                  "inner x"))
             (between (p x y "between"))
             (y (begin
                  (p x y "during y")
                  "inner y"))
             (after (p x y "after")))
         (p x y "body"))))))

测试结果:

> (test-let let)
before: x = "outer x", y = "outer y"
during x: x = "outer x", y = "outer y"
between: x = "outer x", y = "outer y"
during y: x = "outer x", y = "outer y"
after: x = "outer x", y = "outer y"
body: x = "inner x", y = "inner y"

> (test-let let*)
before: x = "outer x", y = "outer y"
during x: x = "outer x", y = "outer y"
between: x = "inner x", y = "outer y"
during y: x = "inner x", y = "outer y"
after: x = "inner x", y = "inner y"
body: x = "inner x", y = "inner y"

> (require rnrs/base-6)
> (test-let letrec)
before: x = #<undefined>, y = #<undefined>
during x: x = #<undefined>, y = #<undefined>
between: x = #<undefined>, y = #<undefined>
during y: x = #<undefined>, y = #<undefined>
after: x = #<undefined>, y = #<undefined>
body: x = "inner x", y = "inner y"

> (require rnrs/base-6)
> (test-let letrec*)
before: x = #<undefined>, y = #<undefined>
during x: x = #<undefined>, y = #<undefined>
between: x = "inner x", y = #<undefined>
during y: x = "inner x", y = #<undefined>
after: x = "inner x", y = "inner y"
body: x = "inner x", y = "inner y"

希望这将使let类型之间的区别非常明显. :-)

Hopefully this will make the differences between the let types pretty obvious. :-)

这篇关于为什么不按照词汇上封闭的`define`来实现`let`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆