gpt4 book ai didi

function - w.r.t.的三个问题评价的环境模型

转载 作者:行者123 更新时间:2023-12-05 01:02:50 25 4
gpt4 key购买 nike

我正在阅读有关命令式编程模型的SICP书Here。我无法从两点理解插图:



。从square到“对”的箭头(两个圆圈):该箭头是什么意思?尽管在本节中,箭头始终表示“封闭环境”,但该特定箭头似乎并不指向环境。(square的环境是global env,而不是“ pair”)
下面是一个正确的理解:在过程定义的值中,其“代码文本”部分(左侧的圆圈)内部没有符号的解释。它们只是“文本”。仅在过程应用程序中,它们在应用程序的上下文/环境中才有意义。
如果2是正确的,为什么选择该对environment part中的箭头(右侧
圈)到封闭环境是否必要? (由于没有意义来解释过程定义内部过程代码中符号的含义。)

最佳答案

SICP的箭头符号有点过载。我将引用文本的相关部分来理解该图。


过程对象是一对,其代码指定过程具有一个形式参数,即x和过程主体(* x x)。过程的环境部分是指向全局环境的指针,因为这是在其中评估lambda表达式以生成过程的环境。将过程对象与符号正方形关联的新绑定已添加到全局框架。通常,define通过向框架添加绑定来创建定义。


因此,让我们分析每个箭头。


“全球环境”→正方形。该箭头似乎只是将正方形标记为象征着全球环境。值得注意的是,此环境是自define在全局环境中被调用以来唯一有效的堆栈帧。
“正方形”→两个点。该箭头似乎表明,无论这两个点代表什么,都以在全局环境中找到的名称"square"存储。
左点→“参数” /“主体”。此箭头指示左点是一个“对象”,被认为是要存储两段数据,即“形式参数列表”和“过程主体”。
右点→正方形。此箭头指示右点包含一个指向全局环境的“指针”。




该图为Lisp中的符号如何获得含义提供了高度可操作的POV。特别地,在特定的“上下文”中对符号进行“评估”。上下文是“环境框架”的链接列表,每个列表都包含一组名称→值映射。为了评估一个符号,在链接列表后跟随一个符号,并返回从符号名称映射的第一个值。举例来说,一个例子是

"foo" → { "bar" : 3    →  { "foo" : 8 }   →   { "foo" : 10 }
, "baz" : 4 }


其中评估 foo通过“跳过”第一帧并在第二帧中找到值 8而忽略第三帧,则返回 8。此忽略功能很重要-它表明某些上下文的名称可能会遮盖较大上下文中的值。



因此,这里的整体情况表明:


在全局上下文中调用 define将新名称→值映射添加到全局框架。
存储lambda对象可存储两条信息(两个点)


左圆点包含lambda主体的文本以及被认为是“形式参数”的符号列表。
右点包含对某些堆栈帧的引用,该堆栈帧可能是也可能不是全局帧,尽管它恰好是此图片中的全局帧



最后,我们应该谈谈评估lambda的含义。要评估lambda,必须将其传递给值列表。它使用该输入值列表,并将其与存储的形式参数列表进行匹配,以生成一个新的环境框架,该框架将形式参数映射到输入值。然后,它使用该新框架作为主要框架并使用链接框架作为后续上下文来评估lambda的主体。以图表的方式表示 square看起来像

        +--- Formal parameter list
/ +--- Body of function
| |
(left: (x) (* x x)) (right: {global frame})


然后,当我们像 (square 3)那样评估它时,我们使用 3和形式参数列表创建一个新框架

{ "x" : 3 }


并评估身体。首先,我们查找名称 *。由于它不在我们的新本地框架中,因此我们必须在全局框架中找到它。

"*"   →   { "x" : 3 }   →   { global frame }


事实证明它在那里存在,并且是乘法的定义。因此,我们需要传递一些值,以便我们查找“ x”

"x"   →   { "x" : 3 }   →   { global frame }


由于 x存储在本地帧中,因此我们在此处找到了它,并将 33作为参数传递给找到的乘法函数。

重要的是,局部框架会遮盖全局框架。这意味着,如果 x在全局框架中也具有含义,我们将在评估 square主体的上下文中将其覆盖。



最后,当我被要求在有关“变量”的含义的问题的上下文中回答这个问题时,重要的是要注意,以上内容是变量的非常特殊的语义的非常特殊的实现。从表面上看,您总可以说“ lisp中的变量恰好意味着此过程的发生”。但是,这可能会有些挑战。

“变量”一词的另一种语义(我和许多数学家都赞成)是这样一种想法,即上下文中的变量代表域中特定的,固定的但未知的值。如果我们检查 square主体中lambda的定义

(lambda (x) (* x x))


我们看到这或多或少是该短语的预期语义-在解释 (* x x)时,我们将 x看作是某个值(例如数字),但我们对此一无所知。在解释 (lambda (x) (* x x))时,我们看到为了理解lambda内的短语的含义,我们必须为其提供 x的含义。这大致是到处使用的变量和函数的标准语义。

挑战在于,这里描述的堆栈框架实现也被设置为容易违反这种语义-实际上,在此示例中,它是如此巧妙地做到了。具体来说: define破坏了语义。原因在以下代码片段中很明显

(define foo 3)
foo
(define foo 4)
foo


在此片段中,我们按顺序评估每个短语,并看到变量 foo的(据说是“固定但未知”)值从第2行更改为第4行。这是因为 define允许我们编辑有效的堆栈帧在一个上下文中,而不仅仅是创建一个新的上下文,它像 lambda一样遮盖了旧的上下文。这意味着我们必须考虑变量不是“固定的而是未知的”,而是一系列可变的槽,这些槽不能保证随时间的推移保持其值-这是一种更为复杂的语义,也许应迫使我们将 foo称为“广告位”或“可分配”。

我们还可以将其视为泄漏抽象。我们希望变量具有标准的“固定但未知”语义,但是由于堆栈框架的机制和 define的行为,我们并未完全遵守该含义。

最后,Lisps通常会为您提供一个名为 let的表格,该表格可用于复制前面的示例而不会丢弃变量语义:

(let ((foo 3))
foo
(let ((foo 4))
foo)
foo)


在这种情况下,第2行上的 foo取值为 3,第4行上的 foo存在于不同的变量上下文中,因此仅遮蔽了第2行上的 foo ...从而取不同值固定值 4,最后第5行的 foo再次与第2行的 foo相同,并且取相同的值。

换句话说, let允许我们创建任意的本地上下文(碰巧通过在幕后创建新的堆栈框架,这可能是您期望的)。让我们知道这些语义是安全的黄金法则被称为α转换,这有点不幸。该规则规定,如果您在一个上下文中各处均匀地重命名变量,则程序的含义不会改变。

因此,前面的示例通过α转换在含义上与此示例相同

(let ((foo 3))
foo
(let ((bar 4))
bar)
foo)


由于我们不再需要担心 foo阴影的影响,因此可能会减少一些混乱。



那么我们可以使Lisp的 define语义更安全吗?有点儿。您可能会想到以下转换:


禁止在一组定义中使用循环依赖,例如不允许使用 (define x y) (define y x),而不允许使用 (define x 3) (define y x)
将所有 define移至任何给定上下文(堆栈框架)的开头,并将它们置于依赖关系顺序。
将“ re define”任何变量设置为错误


事实证明,这种转换有点棘手(代码移动很困难,因此可能是循环依赖性),但是如果消除了一些小问题,您会发现在任何情况下,变量只能使用一个固定但未知的变量值。

您还将找到以下内容来保存-下列任何转换形式的程序

(define x ... definition of x ...)
(define y ... definition of y ...)
(define z ... definition of z ...)
... body ...


等效于以下内容

(let ((x ... definition of x ...))
(let ((y ... definition of y ...))
(let ((z ... definition of z ...))
... body ...)))


这是另一种证明我们的简单易懂的“固定不变但数量未知的变量”语义的方法。

关于function - w.r.t.的三个问题评价的环境模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25666056/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com