2012-03-31 46 views
3

当我每一次评估以下表达式我得到的值10计划分配

(((lambda (x) (lambda() (set! x (+ x 10)) x)) 0)) 

但是我只是一个名称提取上述程序修改,并通过10调用foo每次值增量!

(define foo ((lambda (x) (lambda() (set! x (+ x 10)) x)) 0)) 

有人能解释一下吗?

回答

5

您正在调用的函数是一个计数器,每次调用它时都会返回数字10。

在第一种情况下,您每次都创建一个新函数,然后立即调用它,然后放弃函数。所以每次你第一次调用这个计数器的新实例,所以它应该返回10.

在第二种情况下,你创建一次函数并将它赋给一个变量并重复调用同一个函数。既然你调用的是相同的函数,它应该返回10,20,...

2

newacct是正确的,但我想进入(很多)更多的细节,因为这是一些只是让我的头脑很漂亮最近。

我打算使用“环境”和“范围”这两个术语来相当松散地表示本质上是相同的东西。请记住,该方案是一个lexical scope language

当方案计算一个表达式时,它会在当前环境中查找表达式中任何变量的值。如果在当前环境中找不到任何东西,它会在父环境中查找。如果该值不在父环境中,那么它将在下一级查找,直到达到顶级(全局)级别,在该级别中它将找到该值或抛出“未绑定变量”错误。

无论何时您致电define,您都会将符号与该环境符号表上的值相关联。因此,如果您在顶层调用define,则会将条目添加到全局符号表中。如果在过程的主体中调用define,则将在该过程的符号表中添加一个条目。

去想一个过程调用define一个好方法是,你正在创建一个由参数,身体的符号表中的条目,并且过程的环境。例如,程序square将有一个条目是这样的:

(define a 3) 

(define (square x) 
    (* x x)) 

    GLOBAL 
================= 
    a-|-3 
     | 
square-|-{x} 
     | {(* x x)} 
     | {GLOBAL} ---> All the things defined on the global table 

然后如果我打电话给(square a)解释将在其中square定义环境先来看看它会发现,a与关联值3.然后x - > 3在正方形体内并且过程返回9.很酷,有道理。

当我们开始在过程中定义帮助程序时,事情会变得有点麻烦,但是您真正需要记住的是,如果在当前环境中找不到与某个符号相关的任何内容,它将向上移动范围级别直到它。而且,它会在第一场比赛中停止。所以如果有一个当地的x它会比全球的x更喜欢它(相反它会使用当地的x而没有寻找全球性的)。

接下来,请记住,define只是将名称添加到符号表中,但set!是实际更改与符号关联的值的增变器。

所以(define b "blah")在符号表中放入一个条目。 b => "blah"。没有疯狂。 set!会改变实际值:

(set! b "foo") 
b => "foo" 

set!不能添加任何表。 (set! c "bar") => UNBOUND VARIABLE C

这是最重要的区别:set!的行为就像在其他任何过程,如果它没有找到在当前范围内的变量,它会检查逐步更高级别,直到找到一个匹配(或抛出一个错误),但define总是添加一个绑定到它被调用的范围。

好的,所以你了解defineset!之间的区别。好。现在谈论这个问题。

表达(((lambda (x) (lambda() (set! x (+ x 10)) x)) 0)),作为newacct指出的那样,会因为你每次调用一个新的程序,每次返回相同的值。但是,如果您命名它,则可以跟踪调用该过程所创建的环境。

(define foo  <--- associated name on the symbol table 
    (lambda (x) <--- scope where x is defined 
     (lambda()   \ 
      (set! x (+ x 10)) |--- body 
      x))    /
     0)  <--- initial value of x 

所以内lambda存在由第一个其中符号x存在于的0。然后set!查找在符号表x的条目的初始值和找到一个在下一创建的环境内升级。一旦它发现它改变了它,在这种情况下,它将在其中找到的值增加10。真正很酷的部分是,因为您将整个事件与全局符号表中的名称关联起来,所以在每次调用之后,该环境仍然存在!这就是为什么我们可以做一些很酷的事情,比如实现消息传递对象来跟踪和处理数据!

此外,let特殊形式用于此目的的创建,并且可以构建这样的更直观的方式。它应该是这样的:

(define foo  <--- associated name 
    (let ((x 0)) <--- scope where x is defined & initial x value 
     (lambda()   \ 
      (set! x (+ x 10)) |--- body 
      x)))   /