在此论坛上有一个关于此练习的问题,但没有回答我的特定问题。该练习要求绘制环境图
(define x (cons 1 2))
(define z (cons x x))
(set-car! (cdr z) 17)
(car x)
其中cons
,set-car!
和car
被定义为
(define (cons x y)
(define (set-x! v) (set! x v))
(define (set-y! v) (set! y v))
(define (dispatch m)
(cond ((eq? m 'car) x)
((eq? m 'cdr) y)
((eq? m 'set-car!) set-x!)
((eq? m 'set-cdr!) set-y!)
(else (error "Undefined operation -- CONS" m))))
dispatch)
(define (car z) (z 'car))
(define (cdr z) (z 'cdr))
(define (set-car! z new-value)
((z 'set-car!) new-value)
z)
(define (set-cdr! z new-value)
((z 'set-cdr!) new-value)
z)
前两个非常简单。我的问题是关于第三个(set-car! (cdr z) 17)
我获得的环境图就像这样
基于SICP教科书(第3.2.1节):要对参数应用过程,请创建一个新环境,其中包含一个将参数绑定到参数值的框架。该框架的封闭环境是过程指定的环境。
因此(define x (cons 1 2))
创建环境E1。 (define z (cons x x))
创建E2。
以下部分我不太确定,我的想法是:因为过程设置了车!指向全局环境,我认为(set-car! (cdr z) 17)
应该创建包含在global中的E3。按照相同的逻辑,(cdr z)
应该在global下创建E4,因为cdr
也在global下定义并且指向global。
然后,评估(cdr z)
将调用(z 'cdr)
。因为z指向E2,所以E5是在E2下创建的,其功能为主体,形式参数m为'cdr。评估为x,该x在全局环境中具有绑定。
于是正式定车参数! z绑定到x,x的绑定可以通过E3绑定到全局E,新值直接绑定到E3中的17。
然后通过评估(set-car! z new-value)
,(z 'set-car!)
首先评估。当z绑定到指向E1的x上时,将创建E6,并将其形式参数绑定到'set-car!
上,并且将函数主体分配到E1中。返回值是过程set-x!
,其绑定在E1中找到。评估set-x!在E1下创建E7,并将new-value赋给其形式参数v。
我的问题是如何设置-x!查找在单独的环境E3中分配的new-value的值?如果我们跟踪父环境,从E7到E1,再到全局,它将永远不会引导到E3,新值绑定到17。
基于SICP中的句子,应用set-car!
时必须在global下创建E3。一些在线解决方案跳过了全局下E3和E4的创建,而直接在E7中分配了17,我认为这是不正确的。因为在SICP中明确指出,在应用过程时,将在过程指定的环境下创建新的环境。
请帮助我理解这一点。谢谢。
更新
更清楚地说,我将代码转换为python并在PyTutor http://www.pythontutor.com/下运行。我不了解的是在步骤34和35之间,如下图所示
从步骤34中可以看到,setcar(cdr(z), 17)
在全局环境下创建了一个环境,名称为newvalue
绑定到17。在下一步(35)中,对setx
的求值在父项f1下创建了一个单独的环境(由cons(1,2)
创建)。这些对我来说都是清楚的。
我不了解的是在setx
创建的这种环境中怎么可能,可以找到并分配位于单独环境(newvalue
)中的setcar
的绑定到setx
,v
的形式参数为17。
据我从SICP所了解,这些过程将在其自己的环境及其父级中顺序查找名称绑定。但是在这里,setcar
指向的环境独立于setx
指向的环境及其父环境(f1)。在这里如何进行跨环境查找?
下面是可以在PyTutor中通过上面提供的链接测试的python代码。
def cons(x, y):
def setx(v):
nonlocal x
x=v
def sety(v):
nonlocal y
y=v
def dispatch(m):
if m == 'car': return x
elif m == 'cdr': return y
elif m == 'setcar': return setx
elif m == 'setcdr': return sety
else: print("Undefined operation -- CONS", m)
return dispatch
def car(z):
return z('car')
def cdr(z):
return z('cdr')
def setcar(z, newvalue):
z('setcar')(newvalue)
return z
def setcdr(z, newvalue):
z('setcdr')(newvalue)
return z
x = cons(1,2)
z = cons(x,x)
setcar(cdr(z), 17)
car(x)
已更新2
感谢威尔·尼斯(Will Ness)的精彩回答,问题得到了澄清,下面是我对环境图的更新
答案 0 :(得分:2)
使用您的Python代码(我将其视为具有Scheme语义的伪代码),
def cons(x, y):
def setx(v):
nonlocal x
x=v
def sety(v):
nonlocal y
y=v
def dispatch(m):
if m == 'car': return x
elif m == 'cdr': return y
elif m == 'setcar': return setx
elif m == 'setcdr': return sety
else: print("Undefined operation -- CONS", m)
return dispatch
def car(z):
return z('car')
def cdr(z):
return z('cdr')
def setcar(z, newvalue):
z('setcar')(newvalue)
return z
def setcdr(z, newvalue):
z('setcdr')(newvalue)
return z
我们(用伪代码)
# xx = cons(1,2)
Exx = { x=1, y=2, setx={Exx,setx_proc}, sety={Exx,sety_proc},
dispatch={Exx,dispatch_proc} }
xx = Exx.dispatch
设置xx
来保存值,dispatch
过程的闭包及其封闭的cons
环境框架-我们将此闭包称为 Exx
-在x
,y
,setx
,sety
和dispatch
下有条目;在x
处存储了值1
,在y
中存储了值2
;然后,
# zz = cons(xx,xx)
Ezz = { x=Exx.dispatch, y=Exx.dispatch, setx={Ezz,setx_proc}, sety={Ezz,sety_proc},
dispatch={Ezz,dispatch_proc} }
zz = Ezz.dispatch
设置zz
来保存值,dispatch
过程的闭包及其封闭的cons
环境框架-我们将此闭包称为 Ezz
-在x
,y
,setx
,sety
和dispatch
下有条目;在x
的位置存储了xx
,Exx.dispatch
的值,在y
中存储了xx
,Exx.dispatch
的值;然后,
setcar(cdr(zz), 17) # find the value of the argument
1. = setcar(cdr(zz), 17) # find the value of the argument
2. = setcar(cdr(Ezz.dispatch), 17) # zz is Ezz.dispatch !
3. = setcar(Ezz.dispatch('cdr'), 17) # by the def'n of cdr
4. = setcar(Ezz.y, 17) # by the def'n of dispatch
5. = setcar(Exx.dispatch, 17) # in Ezz.y there's Exx.dispatch !
6. = Exx.dispatch('setcar')(17) ; return(Exx.dispatch) # by the def'n of setcar
7. = Exx.setx(17) ; return(Exx.dispatch) # Exx.dispatch to Exx.setx !
8. = Exx.x=17 ; return(Exx.dispatch) # by the def'n of setx
9. = Exx.dispatch # where Exx.x=17, Exx.y=2
对7. Exx.setx(17)
的评估不会不创建任何新的环境框架。该setx
属于Exx
框架,因此是指Exx
下x
的条目。
因此,在 x
环境框架中的Exx
位置 已更新为保留值17
。
然后,然后,
car(xx)
= car(Exx.dispatch)
= Exx.dispatch('car')
= Exx.x
= 17
答案 1 :(得分:1)
不幸的是,在此问题发布之前,我仍然有此练习留下的伤痕。
如果有帮助,这是我的图表,我认为它与上面的问题一致,主要区别是试图画出过程和对其引用之间的所有界限。
para: x para: z
para: y para: z para: z para: new-value
(define (set-x!... (z 'car) (z 'cdr) ((z 'set-car!)...
^ ^ ^ ^
│ │ │ │
@ @ ─┐ @ @ ─┐ @ @ ─┐ @ @ ─┐
^ │ ^ │ ^ │ ^ │
global env ──┐ │ │ │ │ │ │ │ │
v │ v │ v │ v │ v
┌──────────────────────────────────────────────────────────────────────────┐
│cons:───────────┘ │ │ │ │
│car:───────────────────────────────┘ │ │ │
│cdr:────────────────────────────────────────────┘ │ │
│set-car!:───────────────────────────────────────────────────────┘ │
│ │
│(after calls to cons) │
│x:┐ z:┐ │
└──────────────────────────────────────────────────────────────────────────┘
┌─┘ ^ │ ^
│ │ │ │
│ ,───────────────────────────────────────────────<──┐ │
│/ │ │ │ │
│ ,────────────────────────────────────────────<──┐ │ │
│/ │ │ │ │ │
│ │ │ │ │ │
│ call to cons │ │ │ │ call to cons │
v ┌────────────────────────┴──┐ │ ┌────────────────────────┴──┐
│ │x: 1 (17 after set-x!) │ │ │x:─┘ │ │
│ E1 ->│y: 2 │ │ E2 ->│y:────┘ │
│ │set-x!:────────────────┐ │ │ │set-x!:────────────────┐ │
│ │set-y!:─────────┐ │ │ │ │set-y!:─────────┐ │ │
│ │dispatch:┐ │ │ │ │ │dispatch:┐ │ │ │
│ └───────────────────────────┘ │ └───────────────────────────┘
│ │ ^ │ ^ │ ^ │ │ ^ │ ^ │ ^
├──>─────────────┤ │ │ │ │ │ └───┬──>─────────┤ │ │ │ │ │
│ v │ v │ v │ │ v │ v │ v │
│ @ @ │ @ @ │ @ @ │ │ @ @ │ @ @ │ @ @ │
│ │ └─┘ │ └─┘ │ └─┘ │ │ └─┘ │ └─┘ │ └─┘
│ │ │ │ │ │ │ │
│ ├──────────────────────────────────────┘ │ │
│ │ └───────────────────────────┬──────────┘ │
│ │ └────────────────────│───────────────┬─┘
│ │ │ │ │
│ v │ v v
│ parameter: m │ parameter: v parameter: v
│ (define (dispatch m) │ (set! x v) (set! y v)
│ (cond ((eq? m 'car) x) │
│ ((eq? m 'cdr) y) │
│ ((eq? m 'set-car!) set-x!) │
│ ((eq? m 'set-cdr!) set-y!) │
│ (else ... ))) │
│ │
│ │
│ ┌─────────────────────────────>──┘
│ │
│ │ call to cdr
│ ┌───────────────────────────┐
│ │z:┘ │
│ E3 ─>│ ├─> global env
│ │ │
│ └───────────────────────────┘
│
│
│ call to z (dispatch)
│ ┌───────────────────────────┐
│ │m: 'cdr │
│ E4 ─>│ ├─> E2
│ │ │
│ └───────────────────────────┘
│ (returns 'x' (E1 dispatch))
^
│
├─────────┐
│ │ call set-car!
│ ┌───────────────────────────┐
│ │z:┘ │
│ E5 ─>│new-value: 17 ├─> global env
│ │ │
│ └───────────────────────────┘
│
│
│ call to z (dispatch)
│ ┌───────────────────────────┐
│ │m: 'set-car │
│ E6 ─>│ ├─> E1
│ │ │
│ └───────────────────────────┘
│
│
│ call to set-x!
│ ┌───────────────────────────┐
│ │v: 17 │
│ E7 ─>│ ├─> E1
│ │ │
│ └───────────────────────────┘
│ (E1 modified)
^
│
└─────────┐
│ call to car
┌───────────────────────────┐
│z:┘ │
E8 ─>│ ├─> global env
│ │
└───────────────────────────┘
call to z (dispatch)
┌───────────────────────────┐
│m: 'car │
E9 ─>│ ├─> E1
│ │
└───────────────────────────┘
(returns 17)