了解评估的环境模型 [英] Understanding the environment model of evaluation

查看:86
本文介绍了了解评估的环境模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在SICP中练习3.20:

Exercise 3.20 in SICP:

绘制环境图以说明对序列的评估 表达式

Draw environment diagrams to illustrate the evaluation of the sequence of expressions

(define x (cons 1 2))
(define z (cons x x))


(set-car! (cdr z) 17)

(car x) 17

使用上面给出的对的过程实现.

using the procedural implementation of pairs given above.

我的眼睛被毁了,所以我不能画画.相反,我将尽我所能尽可能地想象环境模型如何演变.

My eyes are destroyed so I cannot draw. I will instead try to imagine as best as I can how the environment model evolves.

首先,这是过程对的实现.

First, here is the procedural pairs implementation.

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

每个人都有一个指向全局环境的指针.

Each has a pointer to the global environment.

这是我想象中的互动和解决方案.

Here is the interactions and my solution as I imagined it.

(define x (cons 1 2))

应用弊端
缺点创建的环境称为e1-全局环境是封闭环境
x绑定到1
y绑定到2
设置x !,设置y!并且每个派遣都有一个指向e1的指针
在全局环境中,dispatch绑定到名称x

apply cons
cons created environment called e1 - the global environment is the enclosing environment
x bound to 1
y bound to 2
set-x!, set-y! and dispatch each has a pointer to e1
dispatch is bound to the name x in the global environment

(define z (cons x x))

应用弊端
缺点创建e2-全局封闭
关于全局(共享),x绑定到x
关于全局(共享),y绑定到x
设置x !,设置y!并且每个派遣都有一个指向e2的指针
在全局环境中,dispatch绑定到名称z

apply cons
cons create e2 - global is enclosing
x bound to x with respect to global (shared)
y bound to x with respect to global (shared)
set-x!, set-y! and dispatch each has a pointer to e2
dispatch is bound to the name z in the global environment

(set-car! (cdr z) 17)

套车!
订车!创建e3-全局封闭
z相对于global绑定到(cdr z)
应用cdr
cdr创建e4-全局正在封装
z相对于global绑定到z
调度创建e5-封装了e2
m绑定到'cdr
返回关于e2的x.
x与全局(以及与e1有关的x)共享x
回到e3
新值绑定到17
调度创建e6-封装了e2
m必定是'set-car!
设置X!关于e2的返回
套用x-
设置X!创建e7-e2正在封装
新值绑定到17
将关于e2的x设置为17
由于x为e1,因此e2中的x和global中的x共享一个过程对象,该过程对象具有一个指向e1的指针,因此该过程对象的汽车发生了变化.

apply set-car!
set-car! creates e3 - global is enclosing
z is bound to (cdr z) with respect to global
apply cdr
cdr creates e4 - global is enclosing
z is bound to z with respect to global
dispatch creates e5 - e2 is enclosing
m is bound to 'cdr
return x with respect to e2.
x shares x with respect to global (as well as x with respect to e1)
back to e3
new-value is bound to 17
dispatch creates e6 - e2 is enclosing
m is bound to 'set-car!
set-x! with respect to e2 is returned
apply set-x!
set-x! creates e7 - e2 is enclosing
new-value is bound to 17
set x with respect to e2 to 17
since x is e1, x in e2 and x in global share a procedure object that has a pointer to e1, the procedure object's car is changed.

我希望这是可以理解的.我的解决方案正确吗?

I hope this is understandable. Is my solution correct?

推荐答案

以下是您特定问题的答案.

Here's the answers to your specific questions.

为了避免混淆,我将全局变量x重命名为v.

I rename the global variable x to v to avoid confusion.

 (define v (cons 1 2))

应用弊端
缺点创建的环境称为e1-全局环境是封闭环境
x绑定到1
y绑定到2
设置x !,设置y!并且每个派遣都有一个指向e1的指针
在全局环境中,dispatch绑定到名称v

apply cons
cons created environment called e1 - the global environment is the enclosing environment
x bound to 1
y bound to 2
set-x!, set-y! and dispatch each has a pointer to e1
dispatch is bound to the name v in the global environment

正确.

 (define z (cons v v))

应用弊端
缺点创建e2-全局封装
x关于全局(共享)绑定到v
y在全局(共享)方面与v绑定
设置x !,设置y!并且每个派遣都有一个指向e2的指针
在全局环境中,dispatch绑定到名称z

apply cons
cons creates e2 - global is enclosing
x is bound to v with respect to global (shared)
y is bound to v with respect to global (shared)
set-x!, set-y! and dispatch each has a pointer to e2
dispatch is bound to the name z in the global environment

正确.

 (set-car! (cdr z) 17)

套车!
订车!创建e3-全局包含

apply set-car!
set-car! creates e3 - global is enclosing

否.

(在下面,不使用代码格式化降价功能,以将视障人士的噪音降至最低).

首先,评估(cdr z).它等效于(z'cdr). z势必从e2帧调度.收到消息"cdr"后,这将分派到e2的y.该访问 e2环境框架中的y插槽,该插槽保存全局环境中的v值.

First, (cdr z) is evaluated. It is equivalent to (z 'cdr). z is bound to dispatch from e2 frame. This dispatches to e2's y, having received the message 'cdr. This accesses the y slot in e2 environment frame, which holds the value of v from the global environment.

接下来,将评估(set-car!v 17).它等效于((v'set-car!)17). v必然会调度e1帧的 .收到了定车"!消息,它将分派到e1的set-x!功能.因此,这将与e1的set-x!一起调用(set-x!17).依次调用环境框e1中的(设置!x 17).因此,它访问-并且修改-"x"消息.环境框架e1中的插槽!

Next, (set-car! v 17) is evaluated. It is equivalent to ((v 'set-car!) 17). v is bound to dispatch of the e1 frame. Having received the 'set-car! message, it dispatches to e1's set-x! function. This will thus call (set-x! 17) with the e1's set-x!. This in turn calls (set! x 17) within the environment frame e1. Thus it accesses - and modifies - the "x" slot in the environment frame e1!

从现在开始,将来任何用v进行的操作都将反映此更改,因为v指的是框架e1,并且该框架已被更改. e1帧在"x"下的存储值被设置为"0".不再是1,而是17..

From now on, any future operation with v will reflect this change, because v refers to the frame e1, and that frame was changed. e1 frame's stored value under "x" is no longer 1, but 17.

通过访问这些值不会创建新的环境框架.值所引用的隐藏帧将被访问,并且可能会被修改.

No new environment frames are created by accessing these values. The hidden frames referred to by the values are accessed, and possibly modified.

cons创建新的隐藏环境框架,该框架将附加到新创建的"cons"框架中.值(即调度功能).

Only cons creates new hidden environment frames which get attached to the newly created "cons" values (i.e. to the dispatch functions).

首先写了以下内容,以作为说明.不幸的是,我怀疑它对于视线(如果有的话)会更有帮助.它包括逐步的评估过程.

首先,我将重新编写您的cons函数,等效并稍微冗长

I will first re-write your cons function, as the equivalent and just a bit more verbose

(define cons 
   (lambda (x y)
     (letrec ([set-x! (lambda (v) (set! x v))]
              [set-y! (lambda (v) (set! y v))]
              [dispatch 
                  (lambda (m)
                    (cond ((eq? m 'car) x)
                          ((eq? m 'cdr) y)
                          ((eq? m 'set-car!) set-x!)
                          ((eq? m 'set-cdr!) set-y!)
                          (else (error 
                            "CONS: ERROR: unrecognized op name" m))))])
        dispatch)))

更多地强调了它的 value 方面,lambda函数也是值,可以创建,命名和返回它们.现在,以上表示在我们的代码中编写(cons 1 2)与编写

emphasizing more the value aspect of it, that lambda functions are values too, that they can be created, named, and returned. Now, the above means that writing (cons 1 2) in our code is the same as writing

(let ([x 1]
      [y 2])
     (letrec ; EXACTLY the same code as above
             ([set-x! (lambda (v) (set! x v))]
              [set-y! (lambda (v) (set! y v))]
              [dispatch 
                  (lambda (m)
                    (cond ((eq? m 'car) x)
                          ((eq? m 'cdr) y)
                          ((eq? m 'set-car!) set-x!)
                          ((eq? m 'set-cdr!) set-y!)
                          (else (error 
                            "CONS: ERROR: unrecognized op name" m))))])
        dispatch))

,并且在评估 this 时,将创建两个绑定-预留两个位置,一个我们以后可以称为x,另一个称为y –每个都是填充有其对应的值:对于x,它是放置在其中的1,对于y – 2,则到目前为止.

and when this is evaluated, two bindings are created – two places are set aside, one we can later on refer to as x, and the other as y – and each is filled with its corresponding value: for x it is the 1 that is put in there, and for y – 2. So far so good.

然后,输入letrec表单.它创建 its 绑定,创建 its 三个特殊位置,分别命名为set-x!set-y!dispatch.每个位置都填充有其对应的值,即创建的对应的lambda函数.

Then, the letrec form is entered. It creates its bindings, its three special places, named set-x!, set-y!, and dispatch. Each place is filled with its corresponding value, the corresponding lambda function that is created.

这是关键部分:由于是在外部(let ([x 1] [y 2]) ...)内部完成 ,因此这三个函数都知道这两个地方,这两个绑定,对于xy.每当set-x!set-y!dispatch使用xy时,实际上所指的分别是xy的位置.

Here is the crucial part: since it is done inside that outer (let ([x 1] [y 2]) ...) form, each of these three functions knows about those two places, those two bindings, for x and y. Whenever x or y is used by set-x!, set-y!, or dispatch, what is actually referred to is the place for x, or for y, respectively.

这三个功能中的每一个也都知道由(letrec ...)创建的其他两个功能及其自身.这就是letrec的工作方式.使用let,它创建的名称只知道封闭环境.

Each of these three functions also knows about the other two, and about itself, being created by (letrec ...). That's how letrec works. With let, the names created by it only know about the enclosing environment.

在创建了三个函数之后,其中一个dispatch作为整个对象(即原始调用(cons 1 2))的值返回.

And after the three functions are created, one of them, dispatch, is returned as the value of the whole thing, i.e. of the original call (cons 1 2).

我们写了(cons 1 2),并得到了一个值,该过程dispatch知道其他两个过程以及xy这两个值位置.

We wrote (cons 1 2), and got back a value, a procedure dispatch that knows about the other two procedures, and also about the two value places, x and y.

返回值,该过程在letrec创建的环境中被称为dispatch,我们可以通过调用消息作为参数,它读取'car'cdr'set-car!'set-cdr!.没什么.

This returned value, this procedure known as dispatch inside that letrec-created environment, we can call it with a message as an argument, that reads 'car, 'cdr, 'set-car!, or 'set-cdr!. And nothing else.

停止.回退一步. 环境" .由letrec创建的环境" 内部的那个letrec形式创建的letrec创建的环境" .我们可以将其可视化为两个嵌套框.两个嵌套的矩形,最外面的一个由let创建,在其中放置了两个位置(两个隔室或"cells" );内部是由letrec创建的,内部有三个隔间,其中有三个 cells .每个对应于其代码片段(如(let ...)(letrec ...)的代码 form )的框,都会创建绑定" 或名称和位置的关联.

Stop. Go back a step. The "environment". The letrec-created "environment", created by that letrec form inside that let-created "environment". We can visualize this as two nested boxes. Two nested rectangles, the outer one created by let, with two places (two compartments, or "cells") set aside in it; and the inner one, created by letrec, with three compartments, three cells in it. Each box corresponding to its code fragment, code form like (let ...) or (letrec ...), that creates "bindings", or associations of names and places.

实际上,每个这样的盒子"被称为环境 frame ;所有嵌套的盒子(每个都有其单元格)一起称为环境.

And actually, each such "box" is known as an environment frame; and all the nested boxes, each with its cells, together are known as the environment.

每个定义的函数都可以访问框-创建该框的 框-该函数还可以访问所有外框,其创建框恰好封闭在其中.就像代码形式一个位于另一个内部一样.这正是作用域"的含义.表示–已知名称的代码区域,该名称指的是持有值的位置.

Each defined function has access to its box – that box in which it was created – and that function also has access to all the outer boxes in which its creation box happens to be enclosed. Just like the code forms are situated one inside the other. Which is exactly what "scope" means – a region of code where a name is known that refers to a place which holds a value.

盒子里面的盒子盒子里面的盒子...里面有隔层.真的,仅此而已.

Box inside a box inside a box... With compartments in them. Nothing more than that to it, really.

       ________________________________
      |                                |
      | (let   ([ .... ]               |
      |         [ .... ])              |
      |    ______________________      |
      |   | (letrec              |     |
      |   |      ([ .... ]       |     |
      |   |       [ .... ]       |     |
      |   |       [ .... ])      |     |
      |   |   ......             |     |
      |   |   ......             |     |
      |   |   )                  |     |
      |   *----------------------*     |
      |   )                            |
      *--------------------------------*

当返回dispatch值(该名称以该名称存储在内部环境框架中的过程)时,它也具有指向(letrec ...)创建的内部框架的隐藏指针.而且该框架还具有指向封闭框的环境框架的隐藏指针,该环境框架由(let ...)表单创建.

And when the dispatch value is returned, which is a procedure stored under that name in that inner environment frame, it also has a hidden pointer to that inner frame created by (letrec ...). And that frame also has a hidden pointer to its enclosing box's environment frame, created by the (let ...) form.

输入let框(代码区域,即示波器)后,将创建其框架.输入letrec框(范围)时,将创建框架.外框的框架对封闭框的框架一无所知,该框是在其之前创建的.最里面的盒子的框架可以访问它周围所有盒子的所有框架,从紧挨着它的那个开始.因此,这采用了一种由内而外的方式:内盒的框架包含指向外盒框架的指针,而外盒(代码区域或作用域)包含内盒(代码区域)

When the let box (code region, i.e. scope) is entered, its frame is created. When the letrec box (scope) is entered, its frame is created. The outer box's frame knows nothing about the enclosed box's frame, being created before it. The innermost box's frame has access to all the frames of all the boxes around it, starting with the one immediately around it. So this goes in a kind of inside-out manner: the inner box's frame contains a pointer to the outer box's frame whereas the outer box (code region, or scope) contains the inner box (code region).

因此,当我们调用(((cons 1 2) 'set-car!) 17)时,它会逐步解释为

So when we call (((cons 1 2) 'set-car!) 17), it is progressively interpreted as

(((cons 1 2) 'set-car!) 17)
=>
(( {dispatch where {E2: set-x!=..., set-y!=..., dispatch=... 
                    where {E1: x=1, y=2 }}} 'set-car!) 17)
=>
(  {(dispatch 'set-car!)
             where {E2: set-x!=..., set-y!=..., dispatch=... 
                    where {E1: x=1, y=2 }}} 17)
=>
(  {set-x!   where {E2: set-x!=..., set-y!=..., dispatch=... 
                    where {E1: x=1, y=2 }}} 17)
=>
{(set-x! 17) where {E2: set-x!=..., set-y!=..., dispatch=... 
                    where {E1: x=1, y=2 }}}
=>
{(set! x 17) where {E2: set-x!=..., set-y!=..., dispatch=... 
                    where {E1: x=1, y=2 }}}
=>
{(set! x 17) where {E1: x=1, y=2 }}
=>
{E1: x=17, y=2 }
     

由于set!实际上更改了存储在单元格中的值,因此从现在开始在程序的其余部分中可以看到此更改:

Because set! actually changes the value stored in the cell, this change will be visible from now on in the rest of the program:

(define v (cons 1 2))
=>
{dispatch where {E2: set-x!=..., set-y!=..., dispatch=... where {E1: x=1, y=2 }}}
;
((v 'set-car!) 17)
=>
{dispatch where {E2: set-x!=..., set-y!=..., dispatch=... where {E1: x=17, y=2 }}}
;
(v 'car)
=>
({dispatch where {E2: set-x!=..., set-y!=..., dispatch=... where {E1: x=17, y=2 }}} 'car)
=>
{ (dispatch 'car) where {E2: set-x!=..., set-y!=..., dispatch=... where {E1: x=17, y=2 }}}
=>
{ x where {E2: set-x!=..., set-y!=..., dispatch=... where {E1: x=17, y=2 }}}
=>
{ x where {E1: x=17, y=2 }}
=>
17

希望此伪代码足够清楚.接下来,

Hopefully this pseudocode is clear enough. Next,

(define v (cons 1 2))
=>
{dispatch where {E2: set-x!=..., set-y!=..., dispatch=...
          where {E1: x=1, y=2 }}}
;
(define z (cons v v))
=>
{dispatch where {E5: set-x!=..., set-y!=..., dispatch=...
          where {E4: x=v, y=v
          where {E3: v={dispatch where {E2: set-x!=..., set-y!=..., dispatch=...
                                 where {E1: x=1, y=2 }}} }}}}

在这里,我们选择了顶级评估策略,以便每个新的顶级命令的环境框架都被包含在前一个环境中.

Here we chose the top-level evaluation strategy so that each new top-level command's environment frame is enclosed in the preceding one's.

(((z 'cdr) 'set-car!) 17)
=>
...... (z 'cdr)
...... =>
...... {(dispatch 'cdr) where {E5: set-x!=..., set-y!=..., dispatch=...
......     where {E4: x=v, y=v
......     where {E3: v={dispatch where {E2: set-x!=..., set-y!=..., dispatch=...
......                            where {E1: x=1, y=2 }}} }}}}
...... =>
...... { x where {E5: set-x!=..., set-y!=..., dispatch=...
......     where {E4: x=v, y=v
......     where {E3: v={dispatch where {E2: set-x!=..., set-y!=..., dispatch=...
......                            where {E1: x=1, y=2 }}} }}}}
...... =>
...... { v where {E3: v={dispatch where {E2: set-x!=..., set-y!=..., dispatch=...
......                            where {E1: x=1, y=2 }}} }}
...... =>
...... {dispatch where {E2: set-x!=..., set-y!=..., dispatch=... where {E1: x=1, y=2 }}}
...... <=
... ((z 'cdr) 'set-car!)
... =>
... {(dispatch 'set-car!) where {E2: set-x!=..., set-y!=..., dispatch=...
...                               where {E1: x=1, y=2 }}}
... =>
... { set-x! where {E2: set-x!=..., set-y!=..., dispatch=... where {E1: x=1, y=2 }}}
... <=
(((z 'cdr) 'set-car!) 17)
=>
{ (set-x! 17) where {E2: set-x!=..., set-y!=..., dispatch=... where {E1: x=1, y=2 }}}
=>
{ (set! x 17) where {E2: set-x!=..., set-y!=..., dispatch=... where {E1: x=1, y=2 }}}
=>
{ (set! x 17) where {E1: x=1, y=2 }}
=>
{E1: x=17, y=2 }

因此它可以正确找到合适的环境框架E1进行突变(即更改存储在其中的值).

so it correctly finds the appropriate environment frame, E1, to mutate (i.e. to change the value stored there).

这篇关于了解评估的环境模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆