提交 ba3959fe 编写于 作者: W wizardforcel

3.6.2.

上级 fbc275dd
......@@ -533,3 +533,164 @@ y
```
## 3.6.2 Logo 语言
Logo 是 Lisp 的另一种方言。它为教育用途而设计,所以 Logo 的许多设计决策是为了让语言对新手更加友好。例如,多数 Logo 过程以前缀形式调用(首先是过程名称,其次是参数),但是通用的算术运算符以普遍的中缀形式提供。Logo 的伟大之处,是它的简单亲切的语法仍旧为高级程序员提供了惊人的表现力。
Logo 的核心概念是,它的内建容器类型,也就是 Logo `sentence `(也叫作列表),可以轻易储存 Logo 源码,这也是它的强大表现力的来源。Logo 的程序可以编写和执行 Logo 表达式,作为求值过程的一部分。许多动态语言都支持代码生成,包括 Python,但是没有语言像 Logo 一样使代码生成如此有趣和易用。
你可能希望下来完整的 Logo 解释器来体验这个语言。标准的实现是 [Berkeley Logo](http://www.cs.berkeley.edu/~bh/logo.html)(也叫做 UCBLogo),由 Brian Harvey 和他的 Berkeley 学生开发。对于苹果用户,[ACSLogo](http://www.alancsmith.co.uk/logo/) 兼容 Mac OSX 的最新版本,并带有一份介绍 Logo 语言许多特性的[用户指南](http://www.alancsmith.co.uk/logo/LogoUserGuide151.pdf)。
**基础。**Logo 设计为会话式。它的读取-求值循环的提示符是一个问号(`?`),产生了“我下面应该做什么?”的问题。我们自然想让它打印数值:
```logo
? print 5
5
```
Logo 语言使用了非标准的调用表达式语法,完全不带括号分隔符。上面,参数`5`转给了`print`,它打印了它的参数。描述 Logo 程序结构的术语有些不同于 Python。Logo 拥有过程而不是 Python 中等价的函数,而且过程输出值而不是返回值。和 python 类似,`print`过程总是输出`None`,但也打印出参数的字符串表示作为副作用。(过程的参数在 Logo 中也通常叫做输入,但是为了清晰期间,这篇文章中我们仍然称之为参数。)
Logo 中最常见的数据类型是单词,它是不带空格的字符串。单词用作可以表示数值、名称和布尔值的通用值。可以解释为数值或布尔值的记号,比如`5`,直接求值为单词。另一方面,类似`five`的名称解释为过程调用:
```logo
? 5
You do not say what to do with 5.
? five
I do not know how to five.
```
`5`和`five`以不同方式解释,Logo 的读取-求值循环也以不同方式报错。第一种情况的问题是,Logo 在顶层表达式不求值为 None 时报错。这里,我们看到了第一个 Logo 不同于计算器的结构;前者的接口是读取-解释循环,期待用户来打印结果。后者使用更加通用的读取-求值-打印循环,自动打印出返回值。Python 采取了混合的方式,非`None`的值使用`repr`强制转换为字符串并自动打印。
Logo 的行可以顺序包含多个表达式。解释器会一次求出每个表达式。如果行中任何顶层表达式不求值为`None`,解释器会报错。一旦发生错误,行中其余的表达式会被忽略。
```logo
? print 1 print 2
1
2
? 3 print 4
You do not say what to do with 3.
```
Logo 的调用表达式可以嵌套。在 Logo 的实现版本中,每个过程接受固定数量的参数。所以,Logo 解释器在嵌套调用表达式的操作数完整时能够唯一地判断。例如,考虑两个过程`sum`和`difference`,它们相应输出两个参数的和或差。
```logo
? print sum 10 difference 7 3
14
```
我们可以从这个嵌套的例子中看到,分隔调用表达式的圆括号和逗号不是必须的。在计算器解释器中,标点符号允许我们将表达式树构建为纯粹的句法操作,没有任何运算符名称的判断。在 Logo 中,我们必须使用我们关于每个过程接受多少参数的知识,来得出嵌套表达式的正确结构。下一节中,问题的细节会深入探讨。
Logo 也支持中缀运算符,例如`+`和`*`。这些运算符的优先级根据代数的标准规则来解析。乘法和触发优于加法和减法:
```logo
? 2 + 3 * 4
14
```
如何实现运算符优先级和前缀运算符来生成正确的表达式树的细节留做练习。对于下面的讨论,我们会专注于使用前缀语法的调用表达式。
**引用。**一个名称会被解释为调用表达式的开始部分,但是我们也希望将单词引用为数据。以双引号开始的记号解释为单词字面值。要注意单词字面值在 Logo 中并没有尾后的双引号。
```logo
? print "hello
hello
```
在 Lisp 的方言中(而 Logo 是它的方言),任何不被求值的表达式都叫做引用。这个引用的概念来自于事物之间的经典哲学。例如一只狗,它可以到处乱跑和叫唤,而单词“狗”只是用于指代这种事物的语言结构。当我们以引号使用“狗”的时候,我们并不是指特定的哪一只,而是这个单词。在语言中,引号允许我们谈论语言自身,Logo 中也一样。我们可以按照名称引用`sum`过程,而不会实际调用它,通过这样引用它:
```logo
? print "sum
sum
```
除了单词,Logo 包含句子类型,可以叫做列表。句子由方括号包围。`print`过程并不会打印方括号,以维持 Logo 的惯例风格,但是方括号可以使用`show`过程打印到输出:
```logo
? print [hello world]
hello world
? show [hello world]
[hello world]
```
子句也可以使用三个不同的二元过程来构造。`sentence`过程将参数组装为子句。它是多态过程,如果参数是单词,会将它的参数放入新的句子中;如果参数是句子,则会将拼接参数。结果通常是一个句子:
```logo
? show sentence 1 2
[1 2]
? show sentence 1 [2 3]
[1 2 3]
? show sentence [1 2] 3
[1 2 3]
? show sentence [1 2] [3 4]
[1 2 3 4]
```
`list`过程从两个元素创建子句,它允许用户创建层次数据结构:
```logo
? show list 1 2
[1 2]
? show list 1 [2 3]
[1 [2 3]]
? show list [1 2] 3
[[1 2] 3]
? show list [1 2] [3 4]
[[1 2] [3 4]]
```
最后,`fput`过程从第一个元素和列表的剩余部分创建列表,就像这一章之前的`RList` Python 构造器那样:
```logo
? show fput 1 [2 3]
[1 2 3]
? show fput [1 2] [3 4]
[[1 2] 3 4]
```
我们在 Logo 中可以调用`sentence`、`list`和`fput`句子构造器。在 Logo 中将句子解构为`first`和剩余部分(叫做`butfirst`)也非常直接,所以,我们也拥有一系列句子的选择器过程。
```logo
? print first [1 2 3]
1
? print last [1 2 3]
3
? print butfirst [1 2 3]
[2 3]
```
**作为数据的表达式。**祖居的内容可以直接当做未求值的引用。所以,我们可以打印出 Logo 表达式而不求值:
```logo
? show [print sum 1 2]
[print sum 1 2]
```
将 Logo 表示表达式表示为句子的目的通常不是打印它们,而是使用`run`过程来求值。
```logo
? run [print sum 1 2]
3
```
通过组合引用和句子构造器,以及`run`过程,我们获得了一个非常用用国的组合手段,它凭空构建 Logo 表达式并对其求值:
```logo
? run sentence "print [sum 1 2]
3
? print run sentence "sum sentence 10 run [difference 7 3]
14
```
最后一个例子的要点是为了展示,虽然`sum`和`difference`过程在 Logo 中并不是一等的构造器(它们不能直接放在句子中),它们的名称是一等的,并且`run`过程可以将它们的名称解析为所引用的过程。
将代码表示为数据,并且稍后将其解释为程序的一部分的功能,是 Lisp 风格语言的特性。程序可以重新编写自己来执行是一个强大的概念,并且作为人工智能(AI)早期研究的基础。Lisp 在数十年间都是 AI 研究者的首选语言。[Lisp 语言](http://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf)由 John McCarthy 发明,它发明了“人工智能”术语,并且在该领域的定义中起到关键作用。Lisp 方言的“代码即数据”的特性,以及它们的简介和优雅,今天仍继续吸引着 Lisp 程序员。
**海龟图形(Turtle graphics)。**所有 Logo 的实现都基于 Logo 海龟 来完成图形输出。这个海龟以画布的中点开始,基于过程移动和转向,并且在它的轨迹上画线。虽然海龟为鼓励青少年实践编程而发明,它对于高级程序员来说也是有趣的图形化工具。
在执行 Logo 程序的任意实践,Logo 海龟都在画布上拥有位置和朝向。类似于`forward`和`right`的一元过程能修改海龟的位置和朝向。常用的过程都有缩写:`forward`也叫作`fd`,以及其它。下面的嵌套表达式画出了每个端点带有小星星的大星星:
```logo
? repeat 5 [fd 100 repeat 5 [fd 20 rt 144] rt 144]
```
![](img/star.png)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册