学习Emacs Lisp - 基础篇
一切都是List
Lisp是LISt Processing的缩写,它是一门处理list的语言。
在Lisp语言中,数据和代码都是用list表示的。List由单词、数字或者其他list组合,并由一对圆括号左右包裹,元素之间用空白字符隔开。
这是一个list的例子:
'(this list has (a list inside of it))
字符串这么表示:
'(this list includes "text between quotation markds.")
执行程序
在Lisp中,list就是可以被执行的表达式。Lisp解释器这样执行一个list:
- 如果list冠以单引号,则返回list本身
- 否则将list中第一个元素视为command/function,返回运行结果。
- 发送错误消息
执行表达式的方法是C-x C-e,调用函数eval-last-sexp。
例如,这个表达式执行后返回4:
(+ 2 2)
4
但是它执行后返回list本身:
'(+ 2 2)
(+ 2 2)
变量
一个symbol可以同时代表一个函数定义和一个值。例如,buffer-name是个builtin函数,它返回当前buffer的名称,给变量buffer-name赋值并不会影响buffer-name函数的执行,因为他们是相互独立的。
(setq buffer-name 1)
1
(buffer-name)
"*scratch*"
变量本身也是表达式,可以通过evaluate它来查看值,例如:
fill-column
80
设置变量的值用set或setq函数。例如:
(set 'a 4)
4
(set 'flowers '(rose violet daisy buttercup))
(rose violet daisy buttercup)
为什么是a和flowers前面各有一个单引号?
因为,譬如在第一个set的例子中,我们希望传入的第一个参数是a本身,而不是a执行之后的值。单引号传递的意思是不要执行而直接返回它本身,可以理解成C++里面的传递引用而非传递值。
setq和set基本相同,除了下面两点:
- 第一个参数不用单引号也能表达传递引用的意思
- 可以在同一个setq中给多个变量赋值
例如:
(setq carnivores '(lion tiger leopard))
(setq trees '(pine fir oak maple)
herbivores '(gazelle antelope zebra))
虽然我这里用了“赋值”这个词语,但是准确的理解应该是“指向”,trees指向了(pine fir oak maple)。
与Buffer相关的函数
| Function | Description |
|---|---|
| buffer-name | 返回当前buffer的名称 |
| buffer-file-name | 返回当前buffer对应的file的名称 |
| current-buffer | 返回当前buffer |
| other-buffer | 返回除当前buffer外最近使用过的buffer |
| switch-to-buffer | 切换buffer |
定义函数
定义函数的语法:
(defun function-name (arguments...)
"optional-documentation..."
(interactive argument-passing-info) ; optional
body...)一个函数定义有5个部分:
- 函数名 - 函数的符号名称
- 参数列表 - 没有参数也要用空列表
()表示 - 函数文档 - 可选,但强烈建议添加
- 交互表达式 - 让你可以用M-x执行函数
- 函数体 - 函数的代码主体
例子:
(defun multiply-by-seven (number)
"Multiply NUMBER by seven."
(* 7 number))定义Interactive的函数
(defun multiply-by-seven (number) ; Interactive version.
"Multiply NUMBER by seven."
(interactive "p")
(message "The result is %d" (* 7 number)))(interactive “p”)的意思是交互参数作为函数的第一个参数传递,交互参数通过C-u <number>来指定。
例如:C-u 8 M-x multiply-by-seven RET等价于执行(multiply-by-seven 8)
函数体可以包含一条或者多条表达式,函数的返回值等于最后一条表达式的返回值。
let
let用来定义局部变量,变量的作用于仅限于let调用的body部分,语法如下:
(let ((variable value)
(variable value)
...)
body...)例如:
(let ((zebra ‘stripes) (tiger ‘fierce)) (message “One kind of animal has %s and another is %s.” zebra tiger))
同setq一样,let的变量不需要加单引号。
if
if的语法:
(if true-or-false-test
action-to-carry-out-if-the-test-returns-true
action-to-carry-out-if-the-test-returns-false)if的条件测试规则:nil为false,其余所有为true。
nil和()是完全相同的东西。
car, cdr
car返回list的第一个元素(等同与first函数)cdr返回list除第一个外的剩余元素(等同于rest函数)
(car '(rose violet daisy buttercup))
rose
(cdr '(rose violet daisy buttercup))
(violet daisy buttercup)
(first '(rose violet daisy buttercup))
rose
(rest '(rose violet daisy buttercup))
(violet daisy buttercup)学习car和cdr的目的是能够读懂先前的代码,但是自己在写代码的时候应当使用first和rest。
car和cdr都是非破坏性的,他们不会改变list的结构,只是返回其中的部分元素。
cons
cons把一个元素和一个list拼接成一个新的list:
(cons 'pine '())
(pine)
(cons 'pine '(fir oak maple))
(pine fir oak maple)
(cons '(pine fir) '(oak maple))
((pine fir) oak maple)cons不改变原来的list,而是创建一个新的list。
length
计算list的长度:
(length '(pine fir oak maple))
4List的实现
List的实现是一个单项链表。
这里定义了一个名为bouquet的list,其中包括三个元素:
(setq bouquet '(rose violet buttercup))把它表示成链表的形式,如下图所示:
bouquet
|
| -------------- --------------- ----------------
| | car | cdr | | car | cdr | | car | cdr |
-->| rose | o------->| violet | o------->| butter- | nil |
| | | | | | | cup | |
-------------- --------------- ----------------