Programming in Emacs Lisp笔记(一)表处理

一直以来我都在使用Emacs编辑器,使用得越多,越想深入了解Emacs Lisp。在gnu的网站上,我找到了Programming in Emacs Lisp,觉得是很好的学习资料。下面是我的学习笔记贴出来与大家分享。 

这个笔记中,对Emacs Lisp中的一些名词:symbols、form、list等没有统一的叫法,对函数、form等也是混合着在使用,主要是为了能让自己更容易了解。 

笔记正在增长中,笔记的原文是用 emacs muse 书写的,需要的朋友可以给我留言。 

表处理 
Lisp列表 
数字,列表中的列表 

列表里也可以包含数字:(+ 2 2)。 

Lisp里的数据和程序都是相同的方式实现的,他们都是在括号中由单词、数字或者其它列表组成的用空白分隔的列表。因为程序看起来像数据,所以一个程序可以当作数据传递给另一个程序,这是lisp一强非常强大的功能。 
Lisp原子 

Lisp列表中的单词叫原子(意为原子在Lisp列表中不可再分割成更小的单位)。与原子不同,list可以分隔成更小的单位(car cdr & cons)。 

空的列表:(),被称作空列表。与其它的数据类型不同,空列表被同时看作原子和列表。 

与自然界的原子一样,Lisp中的原子这个名称来出现得太早(意指与自然界的原子一样,原子还可以再分割)。Lisp中部分原子,比如数组就可以进行分割。但是这种机制与列表的分隔是不同的。如果依据对列表的分隔方式来说,列表中原子就是不可分隔的了。 
列表中的空白 

额外的空白被用来提高代码的可读性。 

'(this list 
   looks like this) 

与 

'(this list looks like this) 

是相同的。 
列表排版 

在Emacs Lisp mode下,有多种方法来对Lisp语句进行排版。比如,按<TAB>键将自动缩进当前光标所在行到正确的位置。M-C-\可以格式化当前所选区域中的代码。 
运行一个程序 

执行Lisp程序时,将执行下列三者之一: 

   1. 什么都不做,返回列表本身 
   2. 返回错误信息 
   3. 把列表中的第一个符号当作命令执行一些操作 

放在列表前的单引号被称作引用(quote);当用它来处理列表时,它告诉Lisp不要对列表进行处理。但如果列表前没有单引号,则列表前的第一个元素是特殊的,它被当作命令被执行(Lisp中这些命令被称作函数)。列表(+ 2 2)显示也与加引号的列表的不同,Lisp知道需要用+来处理列表中的其它元素:把后面的数字相加。 
生成错误信息 

错误信息是由内置的GNU Emacs debugger生成的。进入debugger后,可以用按键q退出debugger。 
符号名称和函数定义 

Lisp中同一指令可以被绑定到多个名称。 

另一方面,在同一时刻一个符号只允许绑定到一个函数定义上。 

由于Emacs Lisp的庞大,它有一套按照不同函数功能分类的符号命名规则。如:所有处理Texinfo的函数都心textinfo-开头,而处理邮件的函数以rmail-开头。 
Lisp解释器 

Lisp的工作方式:首先,它查看列表前是否有单引号,如果有则解释器给出这个列表。如果没有引号,解释器检查列表中的第一个元素是否有对应的函数定义,如果找到则解释器调用函数定义的指令。否则,解释器将打印出错误信息。 
复杂一点的内容 

Lisp解释器可以对没有单引号且不被括号包围的符号。Lisp解释器将检测符号是否为一个变量。 

一些函数不是普通的方法。被用来处理一些特殊的工作,比如定义一个函数。 

Lisp求值时,将先对列表内部嵌入的列表进行求值,从内向外。 

Lisp解释器工作时从左向右,从一个语句到另一个语句(从上至下)。 
编译(Byte Compiling) 

Lisp解释器可以解释两种类型的代码:人可以读的代码和另一种被你为byte compiled code的代码。编译过的代码执行更快。 

可以用byte-compile-file编译代码。被编译好的字节码文件扩展名为.elc。 
求值 

当Lisp解释器工作于一个语句上时,这个活动的过程被称为求值(evaluation)。求值完成后解释器将返回函数定义的执行结果,或者在函数出错时给出错误信息。 
对内部列表求值 

可以把光标停留在内部列表右括号的后面,按C-x C-e执行。 

(+ 2 (+ 3 3)) 

把光标放在括号后面,或者把光标放在代码下面的空行的行首,都可以得到8。如果用C-x C-e对一个数字求值将得到数字自身,这也是数字与符号的不同。 
变量 

Emacs Lisp中符号可以有一个值绑定到它或者一个函数定义绑定到它。两者不同在于,函数定义是指令的集合。值是可以修改的数字或者其它。符号的值可以是任意的Lisp表达式,比如符号、数字、列表、字符串等。有值的符号通常被称作变量。 

符号可以同时有一个函数定义和值。两个是分开的。例: 

(defun test_f () 
"test2" 
(message "bbb")) 

(setq test_f "124") 

test_f          -> 变量值"124" 
(test_f)        -> 函数调用显示"bbb" 

fill-column一个变量的例子 

变量fill-column,每个Emacs缓冲区,这个符号通常被设置成72或70,但也可能有不同的值。可以用C-x C-e对fill-column这个符号求值。 

符号可以有值绑定到上面,我们可以绑定变量到值、数字、字符串、列表甚至是函数定义。 
函数符号未定义时的错误信息 

当我们对fill-column求值时将得到变量的值时并没有在符号外面添加括号。这是因为我们不打算将符号当作函数的名称。 

如果fill-column是列表中的第一个元素或者唯一的元素,Lisp解释器将查找绑定到符号上的函数定义。但fill-column不是一个函数定义。当我们对 

(fill-column) 

求值时将产生错误信息: 

---------- Buffer: *Backtrace* ---------- 
Debugger entered--Lisp error: (void-function fill-column) 
  (fill-column) 
  eval((fill-column)) 
  eval-last-sexp-1(nil) 
  eval-last-sexp(nil) 
  call-interactively(eval-last-sexp) 
---------- Buffer: *Backtrace* ---------- 

函数fill-column未定义。 

按q退出调试器。 
符号没有值时的错误信息 

例如对 

(+ 2 2) 

中的+号求值(光标停留在+的后面,按C-x C-e)时将产生错误信息: 

---------- Buffer: *Backtrace* ---------- 
Debugger entered--Lisp error: (void-variable +) 
  eval(+) 
  eval-last-sexp-1(nil) 
  eval-last-sexp(nil) 
  call-interactively(eval-last-sexp) 
---------- Buffer: *Backtrace* ---------- 

这个错误信息与上节函数未定义时的不同。表示变量+未定义。 
参数 
参数类型 

传递给函数的数据类型依赖于函数需要使用何种信息。比如+函数需要数字类型的参数。concat需要字符串类型的参数。substring是一个特殊一点的函数(称作原子粉碎机),它能把从原子类型中解析出一部分数据。 

(substring "The quick brown fox jumped." 16 19) 

变量值或者列表当作参数 

例: 

(+ 2 fill-column) 
(concat "The " (number-to-string (+ 2 fill-column)) " red foxes.") 

参数数量 

一些函数可以带多个参数,例如:+、*。 

(+)       => 0 

(*)       => 1 

(+ 3)     => 3 

(* 3)     => 3 

(+ 3 4 5) => 12 

(* 3 4 5) => 60 


使用错误类型的参数 

当传递了错误的参数类型时Lisp解释器将产生错误信息。例如对 

(+ 2 'hello) 

求值的结果: 

---------- Buffer: *Backtrace* ---------- 
Debugger entered--Lisp error: 
         (wrong-type-argument number-or-marker-p hello) 
  +(2 hello) 
  eval((+ 2 (quote hello))) 
  eval-last-sexp-1(nil) 
  eval-last-sexp(nil) 
  call-interactively(eval-last-sexp) 
---------- Buffer: *Backtrace* ---------- 

错误信息的第一部分直截告诉我们参数类型错误(wrong-type-argument。第二个部分看起来有些迷惑number-or-marker-p,这部分告诉了我们+函数所需要的参数类型。 

符号number-or-marker-p说明Lisp解释器检查提供给函数的信息(参数的值)是否是数字或marker(C-@或C-< SPC>设置的位置,mark可以被当作数字进行处理-mark在缓冲区中的字符位置)。Emacs Lisp中+可以将数字和作为数字的marker位置相加。 

number-of-marker-p中的p是早期Lisp程序中的用法。p是'predicate'的简写。是早期Lisp研究者所使用的术语, predicate指明了函数用于决定一些属性是true还是false。因此p告诉我们number-or-marker-p是一个根据参数是否为数字或者marker而返回true或者false的函数。另一个以p结尾的Lisp符号包括zerop,这是一个检查参数值是否为0的函数,listp则是一个检测参数是否为一个列表(list)的函数。 

最后,错误信息的其它部分将显示出符号hello。这是传递给+的参数值。 
message函数 

message函数显示信息到回显区。占位符%s表示字符串,%d为整数。例子: 

(message "This message appears in the echo area!") 
(message "The name of this buffer is: %s." (buffer-name)) 
(message "The value of fill-column is %d." fill-column) 
(message "There are %d %s in the office!" 
         (- fill-column 14) "pink elephants") 
(message "He saw %d %s" 
         (- fill-column 34) 
         (concat "red " 
                 (substring 
                  "The quick brown foxes jumped." 16 21) 
                 " leaping.")) 

设置变量值 

有几种方法给变量赋值。set或setq函数,let函数。 
使用set 

要把符号flowers的值设置为列表'(rose violet daisy buttercup),可以执行下面的语句: 

(set 'flowers '(rose violet daisy buttercup)) 

列表(rose violet daisy buttercup)将显示在回显区。这是set函数的返回值。另一方面符号flowers被绑定到列表;这样符号flower可以看作一个变量,它具有那个列表值。 

在对set语句求值后,就可以对符号flowers求值,它将返回set设置的值。当对: 

flowers 

求值时,回显区将显示(ros violet daisy buttercup)。 

这时如果对'flowers求值,将在回显区看到符号自身flowers。 

当使用set时,需要在两个参数前加单引号,除非你想对它们进行求值。如果没有加单引号,则解释器将先对参数进行求值,例如对flowers求值,如果flowers之前未赋过值,则将报错,如果对flowers的求值返回了值,则后面的变量值将赋给对flowers求值所返回的值上。这种情况非常少见。 

(set 'flowers 'aaa) 
(set flowers "123") 
(message aaa)       ->显示"123" 

使用setq 

setq与set类似,但setq将自动给第一个参数前加单引号。另一方面,setq允许在一条语句中同时设置多个不同的变量值。例: 

(setq carnivores '(lion tiger leopard)) 

与 

(set 'carnivores '(lion tiger leopard)) 

相同。 

setq可以给多个变量赋值,例: 

(setq trees '(pine fir oak maple) 
      herbivores '(gazelle antelope zebra)) 

尽管我们一直在用赋值('assign'),但有另一种方式思考set和setq;即set和setq使一个符号指向(point)一个列表。 
计数器 

这是一个在计数器中使用setq的例子。 

(setq counter 0) ; 初始化 
(setq counter (+ counter 1)) ; 增加 
counter 

小结 

    * Lisp程序由表达式组成,表达式可以是列表或者原子。 
    * 列表由零个或者多个原子或内部列表组成,各元素由空白分隔,被括号包括。列表可以为空。 
    * 原子是多个字符符号,比如:forward-paragraph,单字符比如+,双引号间的字符串,数字。 
    * 对自身求值的数字。 
    * 双引号间的字符串也将对自身求值。 
    * 当对符号自身求值时,将返回它指向的值。 
    * 当对列表求值时,Lisp解释器查看列表中的第一个符号所绑定的函数定义。然后按定义的指令执行。 
    * 单引号,',告诉Lisp解释器应该把后面的表达式按原样返回,不对它进行求值。 
    * 参数是传递给函数的信息。函数是列表中的第一个元素,其它元素被求值并作为参数传递给函数。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值