Skip to content

Commit 6a48dc4

Browse files
committed
typo
1 parent 1b51d92 commit 6a48dc4

File tree

1 file changed

+15
-15
lines changed

1 file changed

+15
-15
lines changed

3.6.md

+15-15
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ Logo 的解释器可以拥有和计算器解释器相同的结构。解析器产
901901

902902
![](img/logo_apply.png)
903903

904-
最终的函数 `logo_apply`接受两种参数:基本过程和用户定义的过程,二者都是`Procedure`的实例。`Procedure`是一个 Python 对象,它拥有过程的名称、参数数量、主体和形式参数作为使用属性`body`属性可以有各种类型。基本过程在 Python 中已经实现,所以它的`body`就是 Python 函数。用户定义的过程(非基本)定义在 Logo 中,所以它的 `body`就是 Logo 代码行的列表。`Procedure`也拥有两个布尔值属性。一个用于表明是否是基本过程,另一个用于表明是否需要访问当前环境。
904+
最终的函数 `logo_apply`接受两种参数:基本过程和用户定义的过程,二者都是`Procedure`的实例。`Procedure`是一个 Python 对象,它拥有过程的名称、参数数量、主体和形式参数作为实例属性`body`属性可以拥有不同类型。基本过程在 Python 中已经实现,所以它的`body`就是 Python 函数。用户定义的过程(非基本)定义在 Logo 中,所以它的 `body`就是 Logo 代码行的列表。`Procedure`也拥有两个布尔值属性。一个用于表明是否是基本过程,另一个用于表明是否需要访问当前环境。
905905

906906
```py
907907
>>> class Procedure():
@@ -926,33 +926,33 @@ Logo 的解释器可以拥有和计算器解释器相同的结构。解析器产
926926
"""Apply a user-defined procedure"""
927927
```
928928

929-
用户定义过程的函主题是代码行的列表,每一行都是 Logo 句子。为了在参数列表上调用过程,我们在新的环境中求出主体的代码行。为了构造这个环境,我们向当前环境中添加新的帧,过程的形式参数在里面绑定到实参上。这个过程的重要结构化抽象是,求出用户定义过程的主体的代码行,需要递归调用`eval_line`
929+
用户定义过程的主体是代码行的列表,每一行都是 Logo 句子。为了在参数列表上调用过程,我们在新的环境中求出主体的代码行。为了构造这个环境,我们向当前环境中添加新的帧,过程的形式参数在里面绑定到实参上。这个过程的重要结构化抽象是,求出用户定义过程的主体的代码行,需要递归调用`eval_line`
930930

931-
**求值/应用递归。**实现求值过程的函数,`eval_line ``logo_eval`,以及实现函数应用过程的函数,`apply_procedure``collect_args``logo_apply`,是互相递归的。无论何时调用表达式被发现,求值需要调用它。应用使用求值来求出实参中的操作数表达式,以及求出用户定过程的主体。这个互相递归过程的通用结构在解释器中非常常见:求值以应用定义,应用又使用求值定义。
931+
**求值/应用递归。**实现求值过程的函数,`eval_line ``logo_eval`,以及实现函数应用过程的函数,`apply_procedure``collect_args``logo_apply`,是互相递归的。无论何时调用表达式被发现,求值操作都需要调用它。应用操作使用求值来求出实参中的操作数表达式,以及求出用户定义过程的主体。这个互相递归过程的通用结构在解释器中非常常见:求值以应用定义,应用又使用求值定义。
932932

933933
![](img/eval_apply.png)
934934

935-
这个递归循环终止于语言的基本类型。求值的基本条件是,求解基本表达式、变量、引用表达式或定义。函数调用的基本条件是调用基本过程。这个互相递归的结构,在处理表达式形式的求值函数和处理函数极其参数的应用之间,构成了求值过程的本质。
935+
这个递归循环终止于语言的基本类型。求值的基本条件是,求解基本表达式、变量、引用表达式或定义。函数调用的基本条件是调用基本过程。这个互相递归的结构,在处理表达式形式的求值函数,和处理函数及其参数的应用之间,构成了求值过程的本质。
936936

937937
## 3.6.4 环境
938938

939-
既然我们已经描述了 Logo 解释器的结构,我们转而实现`Environment `类,便于让它使用动态作用域正确支持赋值、过程定义和变量查找。`Environment`实例表示名称绑定的共有集合,可以在程序执行期间的某一点上访问。绑定在帧中组织,而帧以 Python 字典实现。帧包含变量的名称绑定,但不包含过程。运算符名称和`Procedure`实例之间的绑定在 Logo 中是单独储存的。在这个实现项目中,包含变量名称绑定的帧储存为字典的列表,位于`Environment``_frames`属性中,而过程名称绑定储存在值为字典的`procedures`属性中。
939+
既然我们已经描述了 Logo 解释器的结构,我们转而实现`Environment `类,便于让它使用动态作用域正确支持赋值、过程定义和变量查找。`Environment`实例表示名称绑定的共有集合,可以在程序执行期间的某一点上访问。绑定在帧中组织,而帧以 Python 字典实现。帧包含变量的名称绑定,但不包含过程。运算符名称和`Procedure`实例之间的绑定在 Logo 中是单独储存的。在这个实现中,包含变量名称绑定的帧储存为字典的列表,位于`Environment``_frames`属性中,而过程名称绑定储存在值为字典的`procedures`属性中。
940940

941941
帧不能直接访问,而是通过两个`Environment`的方法:`lookup_variable``set_variable_value`。前者实现了一个过程,与我们在第一章的计算环境模型中引入的查找过程相同。名称在当前环境第一帧(最新添加)中与值匹配。如果它被找到,所绑定的值会被返回。如果没有找到,会在被当前帧扩展的帧中寻找。
942942

943-
`set_variable_value `也会寻找与变量名称匹配的绑定。如果找到了,它会更新为新的值,如果没哟刟,那么会在全局帧上创建新的绑定。这些方法的实现留做配套项目中的练习。
943+
`set_variable_value `也会寻找与变量名称匹配的绑定。如果找到了,它会更新为新的值,如果没有找到,那么会在全局帧上创建新的绑定。这些方法的实现留做配套项目中的练习。
944944

945945
`lookup_variable `方法在求解变量名称时由`logo_eval`调用。`set_variable_value ``logo_make`函数调用,它用作 Logo 中`make`基本过程的主体。
946946

947-
除了变量和`make`基本过程之外,我们的解释器支持它的第一种抽象手段:将名称绑定到值上。在 Logo 中,我们现在可以重复第一章中的,我们的第一种抽象步骤
947+
除了变量和`make`基本过程之外,我们的解释器支持它的第一种抽象手段:将名称绑定到值上。在 Logo 中,我们现在可以重复我们第一章中的第一种抽象步骤
948948

949949
```logo
950950
? make "radius 10
951951
? print 2 * :radius
952952
20
953953
```
954954

955-
赋值只是抽象一种有限总是。我们已经从这门课的开始看到,即使对于不是很大的程序,用户定义函数是个管理复杂性的关键工具。我们需要两个改进来实现 Logo 中的用户定义过程。首先,我们必须描述`eval_definition`的实现,如果当前行是定义,`logo_eval`会调用这个 Python 函数。其次,我们需要在`logo_apply`中完成我们的表描述,它在一些参数上调用用户过程。这两个改动都需要利用上一节定义的`Procedure`类。
955+
赋值只是抽象的一种有限形式。我们已经从这门课的开始看到,即使对于不是很大的程序,用户定义函数也是管理复杂性的关键工具。我们需要两个改进来实现 Logo 中的用户定义过程。首先,我们必须描述`eval_definition`的实现,如果当前行是定义,`logo_eval`会调用这个 Python 函数。其次,我们需要在`logo_apply`中完成我们的描述,它在一些参数上调用用户过程。这两个改动都需要利用上一节定义的`Procedure`类。
956956

957957
定义通过创建新的`Procedure`实例来求值,它表示用户定义的过程。考虑下面的 Logo 过程定义:
958958

@@ -964,13 +964,13 @@ Logo 的解释器可以拥有和计算器解释器相同的结构。解析器产
964964
120
965965
```
966966

967-
定义的第一行提供了过程的名称`factorial`和形参`n`。随后的一些行组成了过程体。这一行并不会立即求值,而是为将来使用而储存。也就是说,这些行由`eval_definition`读取并解析,但是并不传递给`eval_line`。主体中的行一直读取,直到出现了只包含`end`的行。在 Logo 中,`end`并不是需要求值的过程,也不是过程体的一部分。它是个函数定义末尾的语法标记。
967+
定义的第一行提供了过程的名称`factorial`和形参`n`。随后的一些行组成了过程体。这些行并不会立即求值,而是为将来使用而储存。也就是说,这些行由`eval_definition`读取并解析,但是并不传递给`eval_line`。主体中的行一直读取,直到出现了只包含`end`的行。在 Logo 中,`end`并不是需要求值的过程,也不是过程体的一部分。它是个函数定义末尾的语法标记。
968968

969-
`Procedure`实例从这个过程名称、形参列表以及主体中创建,并且在环境中的`procedures`的字典属性中注册。不像 Python,在 Logo 中,一旦过程绑定到一个名称,其它定义都不能复用这个名称。
969+
`Procedure`实例从这个过程的名称、形参列表以及主体中创建,并且在环境中的`procedures`的字典属性中注册。不像 Python,在 Logo 中,一旦过程绑定到一个名称,其它定义都不能复用这个名称。
970970

971-
`logo_apply``Procedure`实例应用于一些参数,它是表示为字符串的 Logo 值(对于单词),或列表(对于句子)。对于用户定义过程,`logo_apply`创建了新的帧,它是一个字典对象,键是过程的形参,值是实参。在动态作用域语言例如 Logo 中,这个新的帧总是扩展自过程调用处的当前环境。所以,我们将新创建的帧附加到当前环境上。之后,主题中的每一行都依次传递给`eval_line `。最后,在主体求值完成后,我们可以从环境中移除新创建的帧。由于 Logo 并不支持高阶或一等过程,在程序执行期间,我们并不需要一次跟踪多于一个环境。
971+
`logo_apply``Procedure`实例应用于一些参数,它是表示为字符串的 Logo 值(对于单词),或列表(对于句子)。对于用户定义过程,`logo_apply`创建了新的帧,它是一个字典对象,键是过程的形参,值是实参。在动态作用域语言例如 Logo 中,这个新的帧总是扩展自过程调用处的当前环境。所以,我们将新创建的帧附加到当前环境上。之后,主体中的每一行都依次传递给`eval_line `。最后,在主体完成求值后,我们可以从环境中移除新创建的帧。由于 Logo 并不支持高阶或一等过程,在程序执行期间,我们并不需要一次跟踪多于一个环境。
972972

973-
下面的例子演示了真的列表和动态作用域规则,它们由调用这个两个 Logo 的用户定义过程产生
973+
下面的例子演示了帧的列表和动态作用域规则,它们由调用这两个 Logo 用户定义过程产生
974974

975975
```logo
976976
? to f :x
@@ -984,7 +984,7 @@ Logo 的解释器可以拥有和计算器解释器相同的结构。解析器产
984984
13
985985
```
986986

987-
从这些表达式求值中创建的环境分为过程和帧,它们维护在分离的命名空间中。帧的顺序由调用顺序决定。
987+
由这些表达式的求值创建的环境分为过程和帧,它们维护在分离的命名空间中。帧的顺序由调用顺序决定。
988988

989989
![](img/scope.png)
990990

@@ -1009,11 +1009,11 @@ Logo 的解释器可以拥有和计算器解释器相同的结构。解析器产
10091009

10101010
![](img/factorial_machine.png)
10111011

1012-
与之相似,我们可以将 Logo 解释器看做非常特殊的机器,它接受机器的描述作为输入。给定这个输入,解释器就能配置自己来模拟做描述的机器。例如,如果我们像解释器中输入阶乘的定义,解释器就可以计算阶乘。
1012+
与之相似,我们可以将 Logo 解释器看做非常特殊的机器,它接受机器的描述作为输入。给定这个输入,解释器就能配置自己来模拟描述的机器。例如,如果我们向解释器中输入阶乘的定义,解释器就可以计算阶乘。
10131013

10141014
![](img/universal_machine.png)
10151015

1016-
从这个观点得出,我们的 Logo 解释器可以看做通用的机器。当输入以 Logo 程序描述时,它模拟了其它机器。它在由我们的编程语言操作的数据对象,和编程语言自身之间起到衔接作用。想象一下,一个用户在我们正在运行的 Logo 表达式中输入了 Logo 表达式。从用户的角度来看,类似`sum 2 2`的输入表达式是编程语言中的表达式,解释器应该对其求值。但是,从解释器的角度来看,表达式只是单词组成的句子,可以根据定义好的一系列规则来操作它。
1016+
从这个观点得出,我们的 Logo 解释器可以看做通用的机器。当输入以 Logo 程序描述时,它就能模拟其它机器。它在由我们的编程语言操作的数据对象,和编程语言自身之间起到衔接作用。想象一下,一个用户在我们正在运行的 Logo 解释器中输入了 Logo 表达式。从用户的角度来看,类似`sum 2 2`的输入表达式是编程语言中的表达式,解释器应该对其求值。但是,从解释器的角度来看,表达式只是单词组成的句子,可以根据定义好的一系列规则来操作它。
10171017

10181018
用户的程序是解释器的数据,这不应该是混乱的原因。实际上,有时候忽略这个差异会更方便,以及让用户能够显式将数据对象求值为表达式。在 Logo 中,无论我们何时使用`run `过程,我们都使用了这种能力。Python 中也存在相似的函数:`eval`函数会求出 Python 表达式,`exec`函数会求出 Python 语句,所以:
10191019

0 commit comments

Comments
 (0)