在这个论坛中有一个关于这个练习的问题,但它没有回答我的具体问题。本练习要求绘制环境图
(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)
应该创建封闭在全球范围内的E3。使用相同的逻辑,(cdr z)
应该在全球范围内创建E4,因为cdr
也在全球范围内定义并指向全球。
然后,评估(cdr z)
调用(z 'cdr)
。因为z指向E2,所以在E2下创建E5,函数调度体和形式参数m为'cdr。这被评估为x,它在全球环境中具有约束力。因此集合车的形式参数! z与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,并将新值分配给其形式参数v。
我的问题是怎么设置-x!找到在单独的环境E3中分配的新值的值?我们追踪从E7到E1然后是全球的父母环境,它永远不会引导E3,其中新值绑定到17。
基于SICP中的句子,在应用set-car!
时,必须在全局下创建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)。如何在这里查看交叉环境?
下面是python代码可以在PyTutor中使用上面给出的链接进行测试。
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的精彩回答,问题得到澄清,下面是我对环境图的更新
使用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
我仍然有这个练习留下的伤疤,不幸的是,我在这个问题发布之前做过。
如果它有任何帮助,这里是我的图表,我认为与上面问题中的图表一致,主要区别在于尝试绘制过程之间的所有行和对它们的引用。
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)