**常规编程。**我们的 Logo 之旅就到此为止了。我们还没有介绍任何高级特性,例如,对象系统、高阶过程,或者语句。学会在 Logo 中高效编程需要将语言的简单特性组合为有效的整体。
**常规编程。**我们的 Logo 之旅就到此为止了。我们还没有介绍任何高级特性,例如,对象系统、高阶过程,或者语句。学会在 Logo 中高效编程需要将语言的简单特性组合为有效的整体。
Logo 中没有条件表达式类型。过程`if`和`ifelde`使用调用表达式求值规则。`if`的第一个参数是个布尔单词,`True`或者`False`。第二个参数不是输出值,而是一个句子,包含如果第一个参数为`True`时需要求值的代码行。这个设计的重要结果是,第二个函数的内容如果不被用到就不会全部求值。
Logo 中没有条件表达式类型。过程`if`和`ifelse`使用调用表达式的求值规则。`if`的第一个参数是个布尔单词,`True`或者`False`。第二个参数不是输出值,而是一个句子,包含如果第一个参数为`True`时需要求值的代码行。这个设计的重要结果是,第二个函数的内容如果不被用到就不会全部求值。
```logo
```logo
? 1/0
? 1/0
...
@@ -773,7 +773,7 @@ div raised a ZeroDivisionError: division by zero
...
@@ -773,7 +773,7 @@ div raised a ZeroDivisionError: division by zero
infinity
infinity
```
```
Logo 的条件语句不仅仅不需要特殊语法,而且它实际上可以使用`word`和`run`实现。`ifelse`的基本过程接受三个函数:不而单词、如果单词为`True`需要求值的句子,和如果单词为`False`需要求值的句子。通过适当命名形式参数,我们可以实现拥有相同行为的用户定义过程`ifelse2`。
Logo 的条件语句不仅仅不需要特殊语法,而且它实际上可以使用`word`和`run`实现。`ifelse`的基本过程接受三个函数:布尔单词、如果单词为`True`需要求值的句子,和如果单词为`False`需要求值的句子。通过适当命名形式参数,我们可以实现拥有相同行为的用户定义过程`ifelse2`。
```logo
```logo
? to ifelse2 :predicate :True :False
? to ifelse2 :predicate :True :False
...
@@ -837,20 +837,20 @@ empty
...
@@ -837,20 +837,20 @@ empty
## 3.6.3 结构
## 3.6.3 结构
这一章描述了 Logo 解释器的普遍结构。虽然这一章是独立的,它也确实引用了配套项目。完成这个项目会从零制造出这一章描述的解释器的有效实现。
这一节描述了 Logo 解释器的通常结构。虽然这一章是独立的,它也确实引用了配套项目。完成这个项目会从零制造出这一章描述的解释器的有效实现。
Logo 的解释器可以共享计算器解释器的相同结构。解析器产生表达式数据结构,可由求值器来解释。求值函数检查表达式的形式,并且对于调用表达式,它在一些参数上调用函数来应用某个过程。但是,还是是一些结构上的不同以适应 Logo 的特殊语法。
Logo 的解释器可以拥有和计算器解释器相同的结构。解析器产生表达式数据结构,它们可由求值器来解释。求值函数检查表达式的形式,并且对于调用表达式,它在一些参数上调用函数来应用某个过程。但是,还是存在一些结构上的不同以适应 Logo 的特殊语法。
**行。**Logo 解析器并不读取一行代码,而是读取可能按序包含多个表达式的整行代码。它不返回表达式树,而是返回 Logo 句子。
**行。**Logo 解析器并不读取一行代码,而是读取可能按序包含多个表达式的整行代码。它不返回表达式树,而是返回 Logo 句子。
解析器实际上只做微小的语法分析。特别是,解析工作并不会将调用表达式的运算符和操作数子表达式区分为树的不同枝干。反之,调用表达式的组件顺序排列,嵌套调用表达式表示为摊平的记号序列。最终,解析工作并不判断基本表达式,例如数值的类型,因为 Logo 没有丰富的类型系统。反之,每个元素都是单词或句子。
解析器实际上只做微小的语法分析。特别是,解析工作并不会将调用表达式的运算符和操作数子表达式区分为树的不同枝干。反之,调用表达式的组成部分顺序排列,嵌套调用表达式表示为摊平的记号序列。最终,解析工作并不判断基本表达式,例如数值的类型,因为 Logo 没有丰富的类型系统。反之,每个元素都是单词或句子。
```py
```py
>>> parse_line('print sum 10 difference 7 3')
>>> parse_line('print sum 10 difference 7 3')
['print', 'sum', '10', 'difference', '7', '3']
['print', 'sum', '10', 'difference', '7', '3']
```
```
解析器做了很魏霞的分析,因为 Logo 的动态特性需要求值器解析嵌套表达式的结构。
解析器做了很微小的分析,因为 Logo 的动态特性需要求值器解析嵌套表达式的结构。
解析器并不会弄清句子的嵌套结构,句子中的句子表示为 Python 的嵌套列表。
解析器并不会弄清句子的嵌套结构,句子中的句子表示为 Python 的嵌套列表。
...
@@ -862,15 +862,15 @@ Logo 的解释器可以共享计算器解释器的相同结构。解析器产生
...
@@ -862,15 +862,15 @@ Logo 的解释器可以共享计算器解释器的相同结构。解析器产生
@@ -893,7 +893,7 @@ Logo 的解释器可以共享计算器解释器的相同结构。解析器产生
...
@@ -893,7 +893,7 @@ Logo 的解释器可以共享计算器解释器的相同结构。解析器产生
return apply_procedure(procedure, line, env)
return apply_procedure(procedure, line, env)
```
```
上面的最后情况调用了第二个过程,它由函数`apply_procedure`来表达。为了调用由运算符记号命名的过程,这个运算符会在当前环境中查找。在上面的定义中,`env`是`Environment `类的实例,会在下一节中描述。`env.procedures`属性是个储存运算符名称和过程之间映射的字典。在 Logo 中,环境拥有单词的这种映射,并且没有局部定义的过程。而且,Logo 为过程名称和变量名称维护分离的映射,叫做分离的命名空间。但是,以这种方式复用名称并不推荐。
上面的最后情况调用了第二个过程,表达为函数`apply_procedure`。为了调用由运算符记号命名的过程,这个运算符会在当前环境中查找。在上面的定义中,`env`是`Environment `类的实例,会在下一节中描述。`env.procedures`属性是个储存运算符名称和过程之间映射的字典。在 Logo 中,环境拥有单词的这种映射,并且没有局部定义的过程。而且,Logo 为过程名称和变量名称维护分离的映射,叫做分离的命名空间。但是,以这种方式复用名称并不推荐。
**过程调用。**过程调用以调用`apply_procedure`函数开始,它被传入由`logo_apply`查找到的函数,并带有代码的当前行和当前环境。Logo 中过程调用的过程比计算器中的`calc_apply`更加通用。特别是,`apply_procedure`必须检查打算调用的过程,以便在求解`n`个运算符表达式之前,判断它的参数数量`n`。这里我们会看到,为什么 Logo 解析器不能仅仅由语法分析构建表达式树,因为树的结构由过程决定。
**过程调用。**过程调用以调用`apply_procedure`函数开始,它被传入由`logo_apply`查找到的函数,并带有代码的当前行和当前环境。Logo 中过程调用的过程比计算器中的`calc_apply`更加通用。特别是,`apply_procedure`必须检查打算调用的过程,以便在求解`n`个运算符表达式之前,判断它的参数数量`n`。这里我们会看到,为什么 Logo 解析器不能仅仅由语法分析构建表达式树,因为树的结构由过程决定。