《GNU Emacs Lisp编程入门》读书笔记

列表处理

Lisp列表

e.g.

'(this list has (a list inside of it))

Lisp原子

在一个列表中,原子是由空格一一分隔的。原子可以紧接着括号。
从技术上说,Lisp中的一个列表有三种可能的组成方式:

  • 括号和括号中由空格分隔的原子
  • 括号和括号中的其他列表
  • 括号和括号中的其他列表及原子

一个列表可以仅有一个原子或者完全没有原子。一个没有任何原子的列表就像这样:(,它被称作空列表。与所有的列表都不同的是,可以把一个空列表同时看作既是一个原子,也是一个列表。
原子和列表的书面表示都被称作
符号表达式**,或者更简单地被称作s-表达式(s-expression)。
在Lisp中,某种类型的原子,例如一个数组,可以被分成更小的部分,但是分割数组的机制与分割列表的机制是不同的。只要是涉及列表操作,列表中的原子就是不可分的。
在Lisp中,所有用双引号括起来的文件,包括标点符号和空格,都是单个原子。这种原子被称作串(String)。字符串是不同于数字和符号的一种原子,在使用上也是不同的。

列表中的空格

列表中空格的数量无关紧要。多余的空格和换行只不过是为了使人们易于阅读而设计的。

运行一个程序

Lisp中的一个列表——任何列表——都是一个准备运行的程序。如果你运行它,计算机将完成三件事: 只返回列表本身;告诉你一个出错消息;或者将列表中的第一个符号当做一个命令,然后执行这个命令。
单引号(’),被称作一个引用(quote)。当单引号位于一个列表之前时,它告诉Lisp不要对这个列表做任何操作,它仅仅是按其原样。
在Emacs可以这样对它求值: 将光标移到正面列表的右括号之后,然后按C-x C-e

产生错误消息

错误消息等价于有助的消息,帮助你排除错误。

符号名和函数定义

在Lisp中,一组指令可以连到几个名字,另一方面,一个符号一次只能有一个函数定义与其连接。

Lisp解释器

Lisp解释器首先会查看一下在列表前面是否有单引号。如果有,解释器就为我们给出这个列表。如果没有引号,解释器就查看列表的第一个元素,并判断它是否是一个函数定义。如果它确实是一个函数,则解释器执行函数定义中的指令。否则解释器就打印一个错误消息。
一种复杂的情况: 除了列表之外,Lisp解释器可以对一个符号求值,只要这个符号前没有引号也没有括号包围它。在这种情况下,Lisp解释器将试图像变量一样来确定符号的值。出现这种情况是因为一些函数异常并且以方式运行。那些函数被弹琴作特殊表(special form)。它们用于特殊的工作,例如定义一个函数。
最复杂的情况:如果Lisp解释器正在寻找的函数不是一个特殊表,而是一个列表的一部分,则Lisp解释器首先查看这个列表中是否有另外一个列表。如果有一个内部列表,Lisp解释器首先解释将如何处理那个内部列表,然后再处理外层的这个列表。如果还有一个列表嵌入在内层列表中,则解释器将首先解释那个列表,然后逐一往外解释。它总是首先处理最内层的列表。解释器首先处理最内层的列表是为了找到它的结果。这个结果可以由包含它的表达式使用。
否则,解释器从左到右工作,一个表达式接一个表达式地进行解释。

字节编译

Lisp解释喊叫可以解释两种类型的输入数据:

  • 人可以读懂的代码
  • 经过特殊处理的、被称作字节编译的代码。
    可以通过运行一个编译命令(如: byte-compile-file)将人能读懂的代码转换成字节编译代码。字节编译代码经常储存在一个文件中,这个文件以".elc"作为扩展名。

求值

当Lisp解释器处理一个表达式时,这个动作被称作”求值“
在解释器返回一个值的同时,它也可以做些其他的事情,例如移动光标或者拷贝一个文件,这种动作称为附带效果。我们认为的重要事情,如打印一个文件,对Lisp解释器而言常是一个附带效果。
总之,对一个符号表达式求值几乎总是使Lisp解释器返回一个值,同时可能产生一个附带效果,不然,就会产生一个错误消息。

对一个内部列表求值

如果是对一个嵌套在另一个列表中的列表求值,对外部列表求值时可以使用首先对内部列表求值所得的结果。这解释了为什么内层列表总是首先被求值的:因为它们的返回值被用于外部表达式。

变量

在Lisp中,可以将一个值赋给一个符号,就像将一个函数定义赋给一个符号那样。一个符号的值可以是Lisp中的任意表达式,如一个符号、一个数字、一个列表或者一个字符串。
有值的一个符号通常被称作一个变量。
一个符号可以同时具有一个函数定义和一个值。这两者是各自独立的。

参量

参数的数据类型

应当传递给函数的数据的类型依赖于它使用的什么信息。

作为变量和列表的值的参量

参量可以是一个符号,对这个符号求值将返回一个值。
另外,参量也可以是一个列表,当求值时这个列表返回一个值。

数目可变的参量

有些函数,如concat, +和*,可以有任意多个参量

用一个错误类型的数据对象作为参量

当函数的一个参量被传送一个错误类型的数据时,Lisp解释器产生一个错误消息。

message函数

像+函数一样,message函数的参量数目也是可以变化的。它被用于给用户发送消息。
e.g.

(message "This message appears in the echo area!!!")

双引号中的整个字符串是一个参量,它被打印出来。

给一个变量赋值

有几种方法给一个变量赋值,其中一种方法是使用set函数或者使用setq函数。
另外一种方法是使用let函数

使用set函数

e.g.

(set 'flowers '(rose violet daxmindxxmisy buttercuup))

符号flowers被绑定到一个列表上,也就是列表作为值被赋给可以被当做变量的符号flowers。
需要注意,当使用set函数时,需要将set函数的两个参量都用引号限定起来,除非你希望它们被求值。

使用setq函数

setq特殊表函数,就像set函数一样,不同之处只在于其第一 个参量自动地带上单引号。另外一个方便之处在于,setq函数允许在一个表达式中将几个不同的变量设置成不同的值。第一个参量绑定到第二参量的值,第三个参量绑定到第四个参量的值,以此类推。

计数

(setq counter 0)
(setq counter (+ counter 1))

小结

  • Lisp程序由表达式组成,表达式是列表或者单个原子。
  • 列表由0个或更多的原子或者内部列表组成,原子或者列表之间由空格分隔开,并由括号括起来。列表可以是空的。
  • 原子是多字符的符号、单字符符号、双引号之间的字符串、或者数字。
  • 对数字求值就是它本身。
  • 对双引号这间的字符串求值也是其本身
  • 当对一个符号求值时,将返回它的值。
  • 当对一个列表求值时,Lisp解释器查看列表中的第一个符号以及绑定在其上的函数定义。然后这个函数定义中的指令将被执行。
    -单引号告诉Lisp解释器返回后续表达式的书写形式,而不是像没有单引号时那样对其求值。
  • 参量是传递给函数的信息。除了作为列表的第一个元素的函数之个,通过对列表的其余元素求值来计算函数的参量。
  • 当对一个函数求值时总是返回一个值(除非得到一个错误消息)。另外,它也可以完成一些被称作附带效果的操作。在许多情况下,一个函数的主要目的是产生一个附带效果。

求值实践

缓冲区名

buffer-name和buffer-file-name这两个函数显示文件和缓冲区之间的区别.
当对(buffer-name)求值时,缓冲区的名称将在回显区中出现。当对(buffer-file-name)表达式求值时,缓冲区所指的那个文件的名称将在回显区中出现。通常情况下,由(buffer-name)返回的名称与(buffer-name)所指的文件名称相同,由(buffer-file-name)返回的名称是文件完整的路径名
文件和缓冲区是两个不同的实体。

  • 文件是永久记录在计算机中的信息(除非你删除了它)。
  • 缓冲区则是Emacs内部的信息,它在Emacs编辑会话结束时(或当取消缓冲区时)就消失了。
  • 缓冲区包含了从文件中拷贝过来的信息,我们称这个缓冲正在“访问”那个文件。这份拷贝正是你加工或修改的对象。对这个缓冲区的改动不会改变那个文件,除非你保存了这个缓冲区。
  • 并不是所有的缓冲区都与文件联系在一起。

获得缓冲区

buffer-name函数返回缓冲区的名称 。为了获得缓冲区本身,需要另外一个函数:current-buffer。如果在代码中使用这个函数,得到的将是这个缓冲区本身。
e.g.

(current-buffer)

另一个相关的函数,是获取最近使用过的缓冲区。other-buffer

(other-buffer)

切换缓冲区

当other-buffer函数被一个函数用作参量时,这个other-buffer函数实际上提供了一个缓冲区。通过使用other-buffer函数和switch-to-buffer 函数来切换到另外一个缓冲区。

(switch-to-buffer (other-buffer))

switch-to-buffer函数完成了两件不同的事情:

  • 切换到Emacs关注的缓冲区
  • 从当前显示在窗口中的缓冲区切换到一个新的缓冲区。

set-buffer只做一件事

  • 将计算机的注意力切换到另外一个不同的缓冲区。屏幕上显示的缓冲区并不改变。

所以switch-to-buffer是对人设计的,set-buffer是对计算机设计的。

缓冲区大小和位点的定位

相关函数: buffer-size, point, point-min和point-max。这些函数给出缓冲区大小及其中的位点的位置等信息。

  • buffer-size: 当前缓冲区的大小,也就是,这个函数舞蹈关于这个缓冲区中字符数的计数。
  • point: 返回一个数字,给出光标所处的位置,即从这个缓冲区首字符开始到光标所在位置之间的字符数。
  • point-min: 它返回在当前缓冲区中位点的最小可能值。这个值一般就是1。

如何编写函数定义

除了一些基本函数是用C语言编写的之外,其他所有函数都是用别的函数来定义的。你将在Emacs Lisp中编写函数定义,并用其他函数作为你的基本构件。

defun特殊表

在Lisp中函数定义,是通过对一个以符号defun开关的Lisp表达式求值而被建立的。因为defun不以通常的方式对它的参量求值,因此它被称为特殊表。
一个函数定义在defun一词之后最多有下列五个部分:

  1. 符号名,这是函数定义将要依附的符号。
  2. 将要传送给函数的参量列表。如果没有任何参量传送给函数,那它就是一个空列表()。
  3. 描述这个函数的文档。(技术上说,这部分是可选的,但是我强烈推荐你使用。)
  4. 一个使用函数成为交互函数的表达式,这是可选的。因此,可以通过键入M-x和函数名来使用它,或者键入一个适当的键或者健序列来使用它。
  5. 指导计算机如何运行的代码,这是函数的定义的主体。

格式:

(defun function-name (arguments...)
    "optional-documentaion..."
    (interactive argument-passing-infoo) ;optional
    BODY..)

e.g.

(defun multiply-by-seven (number)
    "Multipl NUMBER by seven"
    (* 7 number))

安装函数定义

通过将光标置于函数定义的最后一个括号之后,并键入C-x C-e。将安装这个函数。

改变函数的定义

如果要改变multiply-byseven函数中的代码,只需要重写它即可。然后再对新函数定义进行再求值即可。

使用函数成为交互函数

实现:在函数文档后面增加一个以特殊表interactive开始的列表。用户键入M-x和函数名就可以激一个交互函数,或者键入绑定的键序列也可以激活它。
e.g.

(defun multiipy-by-seven (number)
    "multiply NUMBER by seven"
    (intractive "p")
    (mesage "The result s %d" (* 7 number)))

通过将光标置于上面的函数定义之后并键入C-x C-e对其求值,就可以将这个函数定义安装。函数名将显示在回显区中。然后,通过键入C-u和一个数字并键入M-x multiply-by-seven和按下回车键,就可以使用这个函数了。加上了结果的句子”The result is…"将显示在回显区中。
更一般地说,可以用下列两种方法之一激活一个函数:

  • 键入一个包含了传送给函数的数字的前缀参量和M-x以及函数名,如下所示C-u 3 M-x forward-setence;
  • 键入函数绑定键或者键序列,如:C-u 3 M-e

交互的multiiply-by-seven函数 ???

定义如下:

(defun multiply-by-seven(number)
    "multiply NUMBER by seven"
    (interactive "p")
    (mesage "The result is %d" (* 7 number)))

在这个函数中,表达式(inertactive “p”)是由两个元素组成的列表。其中的"p"告诉Emacs要传送一个前缀参量给这个函数,并将它的值用于函数的参量。

interactive函数的不同选项

参考《GNU Emacs Lisp技术手册》

永久地安装代码

当你对一个函数定义求值来安装它时,它将一直保留在Emacs之中直到你退出Emacs为止。你下次再启动一个新的Emacs会话时,除非你再一次对这个函数定义求值,否则这个函数将不会被安装。
在有些时候,你可能要求当你启动一个新的Emacs会话时将函数定义自动安装。可以有如下几种方法:

  • 如果这个要自动安装的代码仅仅供你个人使用的,你可以将这个函数定义的代码放到你的".emacs"初始化文件中。当你启动Emacs时,你的".emacs"文件被自动求值,其中的所有函数定义都会被安装。
  • 你可以将需要自动安装的函数定义放在一个或者多个文件中,然后使用load函数让Emacs对它们求值,从而安装这些文件中的所有函数。
  • 在另一方面,如果有些函数定义是该 计算机的所有用户都要使用的,这种情况下经常将它放到一个叫做“site-init.el"的文件中,这个文件在Emacs启动时被加载。

let函数

let表达式是Lisp中的一个特殊表,用户在绝大多数函数定义中都需要使用它。
let用于将一个符号附着到或者绑定到一个值上,对于这绑定的变量,Lisp解释器就不会将其与函数之外的同名变量混淆了。
let创建的是局部变量,屏蔽了任何在这个let表达式之外同名的变量。局部变量不会影响let表达式之外的东西。
let表达式可以一次创建我个变量。同样,let表达式给每一个变量赋由你创建的一个初始值,或者赋由你给定的一个值,或者赋nil。

let表达式的各个部分

let表达式是一个具有三个部分的列表。

  • 第一部分是let符号
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值