Scheme 方案分配

Scheme 方案分配,scheme,Scheme,当我每次得到值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时计算下面的表达式时

(((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是正确的,但是我想更详细地讲一下,因为这是我最近才想到的

我将非常松散地使用“环境”和“范围”这两个术语,它们的含义基本相同。记住,这个计划是一个好主意

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

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

(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
考虑对过程调用
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
(define foo       <--- associated name
    (let ((x 0))  <--- scope where x is defined & initial x value
        (lambda ()            \
            (set! x (+ x 10)) |--- body
            x)))              /
然后,如果我调用
(square a)
解释器将首先查看定义
square
的环境,它将发现
a
与值3关联。然后x->3在方块体中,程序返回9。酷,有道理

当我们开始在过程中定义helper过程时,事情会变得更加复杂,但您真正需要记住的是,如果在当前环境中找不到任何与符号相关的内容,它将向上移动范围级别,直到找到为止。而且,它总是在第一场“比赛”时停止。因此,如果存在一个本地
x
,它将更喜欢它而不是全局
x
(相反,它将使用本地
x
,而从不寻找全局)

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

所以
(define b“blah”)
在符号表中放入一个条目<代码>b=>“废话”。没什么疯狂的<代码>设置将更改实际值:

(set! b "foo")
b => "foo"
但是
set无法向表中添加任何内容<代码>(设置!c“bar”)=>未绑定变量c

这是最重要的区别:
设置
的行为与任何其他过程类似,如果在当前范围内找不到变量,它将逐步检查更高的级别,直到找到匹配项(或抛出错误),但
define
始终向调用它的范围添加绑定

好了,那么您就明白了
define
set之间的区别了。好。现在进入问题

正如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