博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Emacs之魂(三):列表,引用和求值策略
阅读量:6986 次
发布时间:2019-06-27

本文共 3423 字,大约阅读时间需要 11 分钟。

    • -

回顾

上文我们介绍了Emacs的用法,发现一分钟学会使用它并不是难事,

而且,我们没有让快捷键束缚住,因为Emacs的精髓在于Emacs Lisp中。

本文我们开始探讨Emacs Lisp,不过在这之前我们还要先熟悉一下Lisp的特点和Lisp家族的成员,

随后本文重点分析和介绍了列表,引用和求值策略,
这几个概念,尤其是引用,对学习者来说非常容易引起困惑,
本文采用了不同的角度来描述这些概念。

1. 强大的Lisp

1960年,John McCarthy发表了一篇计算机领域的,这是一篇“惊世之作”,

它的作用简直就像欧几里得《》对几何学的贡献一样。
John McCarthy只用了一些简单的的运算符和函数,构建出了一门的编程语言,
称之为,Lisp是列表处理(List Processing)的简称。
这门语言的关键思想是,不论代码还是数据,都用统一的数据结构(列表)进行表示。

Lisp语言具有很强的表达能力,我们可以用更少的代码做更多的事情。

通常而言,语言具有表达能力就必须提供丰富的内置功能和强大的扩展性。

语言的内置功能指的是语言默认提供的功能,它能减少程序员的重复劳动,帮助他们快速完成工作。

语言的扩展性,指的是当语言内置功能不能满足需要的时候,程序员可以怎样做。
同时具有丰富的功能和强大的扩展性是很困难的,这需要在语言的设计阶段就考虑好,
语言的内置功能越多,就会越复杂,扩展功能的与内置功能的一致性就很难被保证。

现代的高级编程语言,离不开编译器和解释器,

编译器将高级语言的代码转换成更底层的语言,例如C语言或者汇编,
解释器提供了一个运行时环境,直接解释执行高级语言的源代码。

一般而言,编译器是由语言的开发商提供的,使用者并不会参与到编译器的开发工作之中,

如果想要在语言中支持一等函数(first-class function),就必须让语言的开发商改写编译器,
如果需要增加新的类似if-else的控制结构,或者让语言支持面向对象编程,也要改写编译器才行。
因此,语言支持什么功能,以及源代码被如何编译,完全取决于开发商。

而Lisp语言则不同,它允许程序员对编译器进行编程,(

Lisp程序员可以决定代码被如何编译甚至如何被读取,像是半个编译器的开发者一样。

2. Lisp-1和Lisp-2

Lisp语言构成了一个家族,具有成百上千种方言,

用的最多的几种是,Common Lisp,Scheme,Racket,和Emacs Lisp。
其中的目标是简洁,提供了强大的工业级支持,
提供了一种创造语言和设计实践的平台,主要用于Emacs中。

Emacs Lisp更像Common Lisp,它们都是,

即同一个符号在不同的上下文中,可以分别用来表示变量和函数,
而Scheme和Racket则只能用来表示同一个实体,称为Lisp-1。

出现Lisp-2,主要是因为有效率方面的考虑,

在Lisp-2中,函数和变量分属不同的名字空间,在不同的环境中,由不同的求值器进行处理。
这样做也使语义更加复杂了,以后的文章中,我们会介绍Emacs Lisp中符号(Symbol)的概念。

3. 列表对象和它的文本表示

列表是Lisp语言中一种常用的数据结构,用来表示一批数据。

例如,由整数123构成的列表对象,Lisp会将它打印为,(1 2 3)
各个列表元素用空格分隔,用圆括号括起来。

然而,在Lisp代码中直接写(1 2 3),并不会创建一个列表对象,

因为Lisp程序也是用括号方式表示的,例如,(+ 1 2)表示对整数12进行加法运算。

那么如何才能创建一个列表对象呢?

我们需要调用list函数,(list 1 2 3),这段代码将会创建一个由整数123构成的列表对象,
这个列表对象打印为(1 2 3)

注意,以上我们严谨的区分了Lisp对象和它的打印结果,

是因为对象和它的文本表示(textual representation)是不同的概念。

例如,在C语言中我们写,

int result = 1 + 2;

我们实际上是用“1”表示了整数1,“1”只是一段文本,是印刷符号,而整数1是一个数学对象,

同样的,“+”是一段文本,它表示了加法运算符。

在Lisp语言中,我们用文本来写程序,而Lisp读取器得到的是Lisp对象,

经过对这些Lisp对象进行计算,得到了计算结果,也是一个Lisp对象,
最终,反馈给我们的是这个对象的文本表示。

4. 字面量和引用

在Lisp中,我们用文本“1”可以直接表示整数1,用“#t”表示真值,

类似的“1”和“#t”,称之为对象的字面量表示(literal representation)。

其它语言中,也提供了广泛的字面量表示法,例如,JavaScript提供了数组和对象字面量,

const obj = {    x: 1,    y: [2, 3, 4]};

这段代码创建了一个JavaScript Object,它的x属性值是1y属性值是一个数组。

字面量表示法,使得我们不必调用new Objectnew Array来创建它。

Lisp中列表对象用的非常多,每次都使用list函数来创建是一件麻烦的事情,

因此,Lisp语言提供了列表对象的字面量写法,我们只需要调用quote就可以了。

(quote (1 2 3))

以上Lisp代码会创建一个打印形式为(1 2 3)的列表对象。

对于嵌套列表,使用quote是非常方便的,

(quote (1 (2 3) ((4 5) (6 7))))

像这样创建列表的方式,称为引用(quoting),

这不同于按引用调用(call by reference)中的“引用”(reference)。

quote还有一个便捷的写法,就是用单引号来表示它,(quote (1 2 3))可以表示为,

'(1 2 3)

我们只需要在列表前加一个单引号即可,因为列表的右括号表明了它在引用这个列表。

5. quote和list

值得一提的是,引用并不保证每次都会重新创建列表。

例如,在Emacs Lisp中我们使用defun创建函数,

(defun foo ()    '(1 2 3))

然后,用以下方式进行函数调用,注意foo参数个数为0个,

(foo)

多次调用foo,编译器可能返回同一个列表对象。

list则不同,每次调用它会返回一个全新的列表对象,

(defun foo ()    (list 1 2 3))

6. 求值策略

Lisp代码是由表达式构成的,Lisp程序的执行过程就是表达式的求值过程,

(* (+ 1 2) (+ 3 4))

以上表达式的求值结果为21

在程序的列表表示法中,从左到右位于第一个位置的元素,是比较特殊的,
它表示一个函数(function),一个宏(macro),或者一个内置的特殊命令(special form)。
位于其他位置的元素称为参数。

函数被调用的时候,它的每个参数都必须首先被求值,

例如,以上程序中*+都是函数,
在调用乘法函数*时,它的参数(+ 1 2)(+ 3 4)都首先要被求值,分别求值为37
然后再进行乘法运算,结果为21

而宏和内置的特殊命令,并不要求如此,它们有自己的对参数的求值策略。

其中“1”称之为自求值对象,对它进行求值将得到它本身,

1(eval 1)(eval (eval 1))

其结果都为1,其它的自求值对象还包括布尔值,字符串,向量,等等。

(+ 1 2)12之前没有quote,是因为它们是自求值对象,(+ '1 '2)(+ 1 2)的计算结果是相等的。

7. 在Emacs中求值表达式

Emacs可以直接求值文本中的Lisp代码,我们只需要将光标定位到列表尾部,

然后按快捷键C-x C-e即可。(指的是按Ctrl+x,然后再按Ctrl+e

我们还可以试试quote和自求值对象,1求值为1'1求值为1

然而''1却求值为(quote 1),是因为''1实际是(quote (quote 1))

它表示用字面量方式创建了一个形如(quote 1)的列表对象。

下文,我们来讨论Emacs Lisp的控制结构和基本的数据类型,

使用Lisp编程是一件有趣的事情。

参考

转载地址:http://wuhpl.baihongyu.com/

你可能感兴趣的文章
JAVA 序列化(转)
查看>>
随手笔记NO.4
查看>>
未来监护人:FCC专员正在努力推迟网络中立投票
查看>>
LVS负载均衡群集(LVS-NAT)
查看>>
当区块链走进象牙塔:耶鲁系与浙大系首度碰撞
查看>>
ssh配置免密码登录,一句搞定。
查看>>
nodejs渐入佳境[16]-node express项目部署到heroku
查看>>
nodejs渐入佳境[21]-express+mondoDB应用部署到heroku
查看>>
营销圈带你看比伯结婚在中国引起热论背后的故事
查看>>
别急!新鲜的云硬盘让它再凉一会
查看>>
【Zabbix】如何搭建memcached?并使用Zabbix监控memcached?
查看>>
完爆 Best Fit,看阿里如何优化 Sigma 在线调度策略节约亿级成本
查看>>
spring事务
查看>>
NoSQL最新现状和趋势:云NoSQL数据库将成重要增长引擎
查看>>
python系统监控--转载
查看>>
2019年我国云计算行业存在的问题和发展趋势
查看>>
MySQL 每秒 570000 的写入,如何实现?
查看>>
DXF图纸打开后怎么改变保存其格式为DWG?
查看>>
zabbix 监控 介绍
查看>>
SVN使用
查看>>