当我每一次评估以下表达式我得到的值10计划分配
(((lambda (x) (lambda() (set! x (+ x 10)) x)) 0))
但是我只是一个名称提取上述程序修改,并通过10调用foo每次值增量!
(define foo ((lambda (x) (lambda() (set! x (+ x 10)) x)) 0))
有人能解释一下吗?
当我每一次评估以下表达式我得到的值10计划分配
(((lambda (x) (lambda() (set! x (+ x 10)) x)) 0))
但是我只是一个名称提取上述程序修改,并通过10调用foo每次值增量!
(define foo ((lambda (x) (lambda() (set! x (+ x 10)) x)) 0))
有人能解释一下吗?
您正在调用的函数是一个计数器,每次调用它时都会返回数字10。
在第一种情况下,您每次都创建一个新函数,然后立即调用它,然后放弃函数。所以每次你第一次调用这个计数器的新实例,所以它应该返回10.
在第二种情况下,你创建一次函数并将它赋给一个变量并重复调用同一个函数。既然你调用的是相同的函数,它应该返回10,20,...
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
总是添加一个绑定到它被调用的范围。
好的,所以你了解define
和set!
之间的区别。好。现在谈论这个问题。
表达(((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))) /