Pulpcode

捕获,搅碎,拼接,吞咽

0%

实用common lisp 教程 读书笔记4

Start

在经过上一章云里雾里的commonlisp实例代码之后,接下来的几章,作者打算对common进行系统化的介绍。
这一张介绍语法和语义。

4.1 括号里都可以有什么?

作者在这里提出lisp的语法使用大量的括号,而没有类似于我们常见编程语言语法是因为这正是lisp的优势。
作者在接下来也就对这一观点进行了论述。

4.2 打开黑箱

这里作者说典型的任务分为三部分,基本就是编译阶段的
词法分析,语法分析,语义分析。
作者在这里将“语义分析”称之为,求值器。
作者将普通语言中的,编译的这三个部分,称之为一个黑箱。
也就是说,词法分析器Scanner,语法分析器GrammarParser和语义分析器,统称为一个黑箱。
作者之所以这样说,是因为对于程序员而言(而非语言的设计者),即使你知道中间过程有这些产物,这些步骤,但是你并不能对他们做些什么。

作者的原话:

由于语言处理器是一种黑箱,所以处理器所使用的包括语元和抽象语法树在内的数据结构,只对语言的实现者有用。

common的语言处理器分为两部分: 读取器和求值器

读取器:定义了 字符串 如何被转换成 S-表达式
求值器:定义了 S-表达式之上的Lisp形式(form)的语法

这里的lisp形式,其实就是指合法的lisp表达式。

黑箱划分的后果:
1. 可以将S-表达式用作一种可暴露的数据格式来表达源代码之外的数据.
这其实就是在第三章中,作者可以直接从文本文件中读取这些S-表达式。
2. 可以使用语言本身,而非字符串的形式来生成代码。
这是在说,我们非lisp的语言的代码,不像是lisp代码这样,语义本身就是对象树构成的。作者也提到了,这就是Lisp宏的本意。

作者在这里提出了他将集中去解读两个层面:

读取器理解 S-表达式
求值器理解 Lisp形式的S-表达式

那么我们可以这样理解,对于这两个阶段,lisp解释器做了什么?我们可以做什么。

文本 –> 读取器 –> S-表达式 –> Lisp形式的语法 –> 求值器

S-表达式的基本元素是列表和原子。

4.3 S-表达式

读取器将名字转化为符号对象。

之后作者介绍了读取器的工作原理:

读取器将名字转化成符号对象:

特征1:转化成它们等价的大写形式.
特征2:放入成为包的表中。

4.4 作为Lisp形式的S-表达式(这里讲求值器)

求值器像一个函数,接受一个句法良好定义的lisp形式作为参数并返回一个值。也就是分别对原子和列表进行求值。

看作者的这两句话:

Lisp形式的有趣之处不在于其语法,而在于它们被求值的方式。
当我们开始考虑列表的求值方式时,事情变得更加有趣了。

我可以感觉到,作者对宏是多么的喜爱。

4.5 函数调用

函数调用算是比较常见的求值方式了。

(function-hame argument*)将参数求值后,传递给函数

4.6 特殊操作符

通过之前的介绍,函数在被调用之前,所有参数都将被求值。

所以if语句你就无法写成一个函数,这种逻辑基本都使用到短路求值。if 语句就是lisp为了解决这种问题,而提供的“特殊操作符”。

作者在这里指出,common-lisp有25个特殊操作符,但是只有很少一部分是直接常用的。比如QUOTE就算是一个常用的操作符,它用来引用一个列表,而不是对它求值。

(quote (+ 1 2))就可以简写成'(+ 1 2)

可以这样理解特殊操作符,我们的lisp求值器对Lisp形式进行求值。默认情况下,lisp求值器会把第一个位置当作函数,然后从左向右来对每一个参数进行求值,等每个值都被求出来之后,将它们传递给参数。

默认的解析方式,如果不能满足你的需求,那么你就需要一些手段,来控制这棵树的生长:

比如:'(1 2 3) 这样则引用一个列表,而不是对其进行求值。

还有就是本节提到的if,可以通过分支选择。来决定对两个表达式的其中之一进行求值。

最后作者提到,LET修改了其他形式的求值环境,这些将是第6章的重点,不过我并不明白下面例子的含义。
我只能理解为一种特殊操作符的扩展了。

4.7 宏

作者是这样描述宏的:
它以S-表达式为其参数的函数,并返回一个lisp形式,

特殊操作符,可以扩展lisp的语法。但是确实固定的,而宏却能够让用户自己扩展语法。

宏是一个以S-表达式为其参数的函数,并返回一个lisp形式。然后对其求值并用该值取代宏形式。

宏求值包括两部分:

1.宏形式的元素不经过求值传送到宏函数里。
2.宏函数的返回形式,按照正常的求值规则进行求值。

之后作者谈到了在编译lisp代码时,宏的编译过程。

这里作者提到,宏在编译时生成展开式,所以比函数调用付出的代价要小。

作者将宏比作嵌入编译器的钩子。

作者在备注中解释了,可以从代码格式,命名约定,区分,代码结构区分宏和函数

NIL既是一个原子,又是一个列表。所以NIL也就是’()
与t类似,它与它的引用完全相同,之后作者引出了等价谓词。

EQ:只有当两个对象相同时,才是EQ等价的。
EQL:EQ不能保证两个对象表示相同的数字或字符值时等价,但是EQL可以。
作者提到了使用EQ和EQL的两种阵营,而本书就是第二种阵营的。

之后简单的介绍了其它两种宽松的EQUAL和EQUALP等价

这里我总结一下三种求值方式:

函数: GETF
特殊操作符: IF, QUOTE, LET
宏: PUSH, POP, DOLIST, LOOP, SETF

你需要理解,作者指的lisp形式,是什么。

最后,作者介绍了格式化lisp代码,对于lisp这样的“括号语言”,良好的格式化在必要不过了。
因为s-表达式是“一棵树”,所以在同一层的书,在格式化代码的缩进处,也应该是同一缩进。

总结:

  1. 怎么说呢,如果我想写一篇既能自己总结,又能让读者看懂的lisp博客,以现在的水平是做不到的。
    每次读自己的博客,都会发现能修改的地方太多了。
  2. 作者并不会为了底层而底层,给你讲一大堆细节,一大堆原理,而是从对比和使用的角度,为你分析这背后真正的意义。