首页 文章

lambda如何在方案中处理其体内的未绑定变量?

提问于
浏览
0

在此代码段中:

(begin
  (define f '())
  ((lambda ()
     (set! f (lambda (x) (g x 5)))))
  (define (g x y) (+ x y))
  (f 5))

当评估 (set! f (lambda...)) 时,变量g不绑定到任何位置 . 正如R5RS的规范(链接here)所说:

评估lambda表达式时生效的环境将作为该过程的一部分被记住 . 当稍后使用一些实际参数调用该过程时,将通过将形式参数列表中的变量绑定到新位置来扩展评估lambda表达式的环境,相应的实际参数值将存储在这些位置中,并且lambda表达式主体中的表达式将在扩展环境中按顺序计算 .

其中“有效环境”定义为:

命名位置的标识符称为变量,并且被称为绑定到该位置 . 在程序中的某个点上有效的所有可见绑定的集合被称为此时有效的环境 .

所以内在的lambda应该只捕获像 {f: (location #1)} 这样的环境;当通过调用 (f 5) 进行评估时,用于评估其正文的环境应为 {f: (location #1), x: (location #2)} ,其中不包含 g .

但DrRacket(以及petite,这是一个R6RS实现)在评估上述片段时提供了10个 . 所以环境确实包含 g . 为什么?

=====

似乎该方案要求define语句仅出现在 <body> 的开头 . 但是这段代码片段也会返回10:

(begin
  (define (f x) (g x 5))
  (define (g x y) (+ x y))
  (f 5))

2 回答

  • 1

    我想我找出了原因 .

    R5RS说:

    A <body> containing internal definitions can always be converted into
    a completely equivalent letrec expression
    

    其中letrec被描述为:

    The <variable>s are bound to fresh locations holding undefined values,
    the <init>s are evaluated in the resulting environment (in some
    unspecified order), each <variable> is assigned to the result of the
    corresponding <init>, the <body> is evaluated in the resulting
    environment
    

    对于顶级定义:

    Scheme的某些实现使用初始环境,其中所有可能的变量都绑定到位置,其中大多数包含未定义的值 . 这种实现中的顶级定义实际上等同于赋值 .

    这样的行为是可以接受的 .

  • 1

    因此,R5RS因此意味着

    (begin
       (define f '())
       ((lambda () (set! f (lambda (x) (g x 5))) ) )
       (define (g x y) (+ x y))
       (f 5) )
    

    意思是一样的

    (letrec
        ((f '())
         (g +)
         (x x)
         (y y) )
     (f 5) )
    

    或者,更简单

    (let*
        ((g +)
         (f (lambda (x) (g x 5)) ) )
      (f 5) )
    

    在我的(有限)经验中,首先使用"let"绑定表单( letlet* , letrec )进行编程总是更加清晰,并尽可能避免使用 set! . 当我这样做时,我发现我的程序更容易理解,特别是几个月后我回来看它们!

相关问题