python - SICP 练习 3.20 - 理解环境图(我的图中缺少绑定(bind))

标签 python scheme computation-theory sicp lexical-scope

在这个论坛上有一个关于这个练习的问题,但它没有回答我的具体问题。本练习要求绘制环境图

(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)我得到的环境图是这样的

enter image description here

基于 SICP 教科书(第 3.2.1 节):要将过程应用于参数,请创建一个包含将参数绑定(bind)到参数值的框架的新环境。这个框架的封闭环境是过程指定的环境。

因此(define x (cons 1 2))创建环境 E1。 (define z (cons x x))创建 E2。

以下部分我不太确定,我的想法是:因为程序设置-车!指向全局环境,我认为(set-car! (cdr z) 17)应该创建包含在全局中的 E3。同理,(cdr z)应该在全局下创建 E4,如 cdr也在 global 下定义并指向 global。

然后,评估 (cdr z)调用 (z 'cdr) .因为z指向E2,所以在E2下创建了E5,函数dispatch的主体,形参m为'cdr.这被评估为在全局环境中具有绑定(bind)的 x。
因此 set-car 的形式参数!是 z 绑定(bind)到 x 的绑定(bind)可以通过 E3 找到全局 E 和 new-value 直接绑定(bind)到 E3 中的 17。

然后通过评估 (set-car! z new-value) , (z 'set-car!)首先评估。由于 z 与指向 E1 的 x 绑定(bind),因此创建 E6 时,其形式参数绑定(bind)到 'set-car!函数体在 E1 中调度。返回值为过程set-x!其绑定(bind)在 E1 中找到。评估 set-x!在 E1 下创建 E7 并将新值赋给它的形参 v。

我的问题是如何设置-x!找到在单独的环境 E3 中分配的新值的值?如果我们跟踪从 E7 到 E1 然后全局的父环境,它永远不会引导到 E3,其中新值绑定(bind)到 17。

根据SICP中的语句,申请set-car!时必须在global下创建E3 .网上有的解决方案跳过了global下E3和E4的创建,直接在E7中赋值17,我认为是不正确的。因为在 SICP 中写得很清楚,当应用一个过程时,会在过程指定的环境下创建一个新的环境。

请帮助我理解这一点。谢谢你。

更新

为了更清楚,我将代码翻译成 python 并在 PyTutor http://www.pythontutor.com/ 下运行。 .我不明白的是在第 34 步和第 35 步之间,如下图所示

步骤 34 :
enter image description here

步骤 35 :
enter image description here

从第 34 步可以看出,setcar(cdr(z), 17)在全局环境下创建了一个名为 newvalue 的环境绑定(bind)到 17。在下一步 (35) 中,setx 的评估在父 f1 下创建了一个单独的环境(由 cons(1,2) 创建)。这些对我来说都很清楚。

我不明白的是,在 setx 创建的这个环境中怎么可能? , newvalue 的绑定(bind)可以找到位于单独环境( setcar )中的它并将其分配给 setx 的形式参数, v ,如 17。

正如我从 SICP 中了解到的那样,这些程序将依次在它们自己的环境和它们的父级中查找名称绑定(bind)。但是在这里,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 的精彩回答,问题得到了澄清,下面是我对环境图的更新

enter image description here

最佳答案

使用您的 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框架,因此指的是 Exxx 下的条目.

所以x放在 Exx 更新环境框架以保存值 17 .

那么,之后,
car(xx)
= car(Exx.dispatch)
= Exx.dispatch('car')
= Exx.x
= 17

关于python - SICP 练习 3.20 - 理解环境图(我的图中缺少绑定(bind)),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51327758/

相关文章:

python - 我如何在 django admin 中创建高级自定义搜索表单并使用 django admins 更改列表显示

Python多处理/线程比虚拟机上的单处理需要更长的时间

python - 如何在Python中执行断言自省(introspection)

clojure - Clojure、Scheme/Racket 和 Common Lisp 之间有什么区别?

performance - 当预期输入较小时,Big Theta Notation 是否是算法效率的有效衡量标准?

python - Tkinter - 在列表框选择时运行事件函数

list - 如何将一个巨大的文件加载到 Racket 中的字符串或列表中?

javascript - javascript 函数表达式是否类似于或基于 s 表达式?

math - 这里写的 R 符号是什么意思?

PHP和mySQL Task Scheduler程序和数据库原理?