方案分配

时间:2012-03-31 10:25:39

标签: scheme

每当我得到值10时,我会评估以下表达式。

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

但是我只是通过使用名称抽象上述过程进行修改,并在每次值增加10时调用foo !!

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

有人可以解释一下吗?

2 个答案:

答案 0 :(得分:5)

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

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

在第二种情况下,您创建一次函数并将其分配给变量并重复调用该函数。由于你正在调用相同的函数,它应该返回10,20,...

答案 1 :(得分:2)

newacct是正确的,但我想进入(很多)更多细节,因为这是最近引起我注意的事情。

我将非常宽松地使用“环境”和“范围”这两个术语,并且意味着基本相同的事情。请记住,方案是lexical scope language

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

每次调用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!是一个实际更改与符号关联的值的mutator。

所以(define b "blah")在符号表中放入一个条目。 b => "blah"。没什么了不起的。 set!会更改实际值:

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

set!无法向表中添加任何内容。 (set! c "bar") => UNBOUND VARIABLE C

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

好的,您了解defineset!之间的区别。好。现在回答这个问题。

newacct指出的表达式(((lambda (x) (lambda () (set! x (+ x 10)) x)) 0))每次都会返回相同的值,因为每次调用一个新的过程。但是,如果您将其命名,则可以通过调用该过程来跟踪创建的环境。

(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)))              /