1 Emacs 工作流程
Emacs 的工作实质是不停的阅读,求值,打印:
阅读 -—> 求值 —> 打印 —> 阅读 —> 求值 —> 打印 ……
比如你在一个表达式的后面按下 C-x C-e 的时候, Emacs 会先读取光标前面的一个 S-表达式, 然后对其求值。如
(+ 12 13)
在上面右括号的后面按下 C-x C-e, Emacs 读取光标前面的一个 S-表达式,是表 (+ 12 13), 然后求值得到 25, 打印在 message 窗口中。13后面按下 C-x C-e 时, Emacs 读取到前面一个S-表达式是符号 13, 由于对原子求值是其本身,所以打印出 13. Emacs 随时都可以将你缓冲区中的一段文字数据,作为一段程序来运行。
2 S-表达式
我们看到,elisp 作为一种表处理语言,主要的数据结构是S-表达式。S-表达式可以是原子,也可以是表。
- 原子: 最小单位,不可再拆分的类型。如基本的数值型, 字符串型, 符号,vector等等。
- 表: 一个表可以递归的定义为括号内零个或n个元素的序列(每个元素是一个原子或一个表)(以空格分隔各元素): (元素1 元素2 … 元素n)
对于表 '(I am "a pig") 来说,有三个原子组成
I, am, "a pig"
而对于表 '(I am (a pig)), 有两个原子 I, am 和一个表 (a pig) 组成。
通常,我们写表前面加一个单引号 "'", 这是函数 quote 的简写形式。函数返回后面的 S-表达式, 可以理解为对 S-表达式 不求值而直接返回打印它本身。
(+ 12 13) => 25 '(+ 12 13) => (+ 12 13)
一个空表既是一个原子,也是一个表:
(atom '()) ;; 原子的判断谓词 => t (listp '()) ;; 表的判断谓词 => t
2.1 求值
对S表达式求值,对原子来说,返回原子的值。如
(setq a 13) ;; 符号 a 的值是整形 12 a => 13
对表来说,求值比较有意思了,会将表中的第一项作为一个函数,后面的参数作为此函数的参数,求值就是执行这个函数。空表返回 nil.
(+ 12 13)
搜索到第一项是 + , 会在内存中找到这函数,传递2个参数12,13,然后这个函数求值为25,返回打印25。如果找不到函数符号,会提示出错 void-function.
3 nil 和 t
Elisp 中并不存在布尔类型,但是可以判断一个对象是不是真值和假值,就是和对象 t, nil 做比较。编程中,有一个规律,判断一个值的真假,和假值做比较,因为和真值做比较会出现意外的结果。和 nil 做比较的函数是 null, 可以 C-h f 查看文档说明。
nil 和 t 是 Elisp 中最重要的两个对象。 这里的真假指的是对象的真假,而非对象的值的真假。0 是真值, 因为 0 是以整形对象存储。""也是真,以字符串对象存储。相反,一个空的对象,nil 和 空表就是假值。记住在 elisp 中,除了 nil 和 '() ,其余都是真值。
(null "") ;; 空字符串是真 => nil (null 0) ;; 0 是真 => nil (if 0 (message "True") (message "False")) => "True" (/= 0 3) => t (null '()) ;; 空表 => t (null '(a b)) => nil
做判断是和假值做比较,那么非假值就可以返回计算结果了:
(and nil 12) => nil (and 0 12) => 12 (or nil (message "如果是假值那么打印我吧")) ;; 这里的 or 就有 if 的味道了 => "如果是假值那么打印我吧"
3.1 谓词
Elisp 中的谓词指的是那些返回的结果是 t 或 nil 的那些函数。 前面的 null, atom, listp 就是谓词。用以判断是否符合某些条件。
(atom '()) ;; 是否是原子 (listp '()) ;; 是否是表 (null 12) ;; 是否是假值 (floatp 12) ;; 是否是浮点型数值 (integerp 12);;是否是整形数值
大多数谓词通常以 p 做后缀.
4 变量
lisp 中符号是指有名字的对象,在内部是由一个表来维护着名字和对象之间的联系。我们可以通过名字来获得对象的值。而且,对一个符号来说,我们可以更改其指向的对象,轻快的如同将一件物体的标签接下来贴到另一个物体上,如此简单! 这个就是变量。
我们设置一个变量:
(set 'foo 12)
就是我们现在内部表中寻找 foo 这个名字,如果找到将其关联的对象指针指向整形12;如果没找到,先创建一个 foo 符号,其对象指针指向 12. 这个操作太普遍了,所以set 有一个简单的变体形式:
(setq foo 12) ;; 不用在写单引号了 (setq foo 12 go "12") ;; setq 还可以给几个变量赋值
5 控制结构
5.1 顺序 progn
(progn BODY…)
progn 按顺序执行其参数语句,返回的是最后一个参数的执行结果
(progn (setq a 12) (message "sdf") (message "last")) => "last"
5.2 循环 while
(while TEST BODY…)
如果 TEST 为真,执行 BODY…。 如此重复,直到 TEST 为假,返回 nil。
(setq a 12) => 12 ;; 循环移植到 a 不大于0 (while (> a 0) (setq a (- a 1))) => nil a => 0
5.3 判断 if
(if COND THEN ELSE…)
如果 COND 为真执行 THEN, 返回 THEN 的计算结果。 如果 COND 为假, 执行 ELSE… ,并返回 ELSE… 最末一个表达式的结果。 THEN 有且只有一个表达式, ELSE… 为0个或一个表达式。如果 COND 为假,且没有 ELSE… 表达式,返回 nil.
(if t (message "真")) => "真" (if nil (message "真")) => nil (if (< 12 0) (message "12>0") (message "12<0")) =>"12<0" (defun g-max (a b) "求最大值" (if (> a b) a b)) (g-max 12 13) => 13
5.4 分支 cond
(cond CLAUSES…)
会尝试每个条件,直到一个匹配。每个 CLAUSES 是形如 (CONDITION BODY…) 的表,BODY… 为0个或多个表达式。如果找到一个 CONDITION 为真的,会计算 BODY… 并返回最末一个结果。反之返回 nil.
(defun age-interval (age) "返回你的年龄区间" (cond ((< age 10) "童年") ((< age 20) "少年") ((< age 35) "青年") ((< age 45) "中年") ((< age 60) "中老年") (t "老年"))) (age-interval 23) => "青年"
6 let 函数
(let VARLIST BODY…)
如 :
(setq a 12) => 12 (let ((a 15) (b 12)) (message "a=%d, b=%d" a b)) => "a=15, b=12" a => 12
可以看到参数列表中的参数绑定不会影响外面的变量,这就是局部变量。
7 函数
函数在 elisp 中也是一个普通对象,我们可以绑定到一个符号上去。而关联的符号名就是这个函数名。
(type-of 'null) => symbol (functionp 'null) => t
一个函数的定义包括名字,参数列表,函数文档,交互选项,函数体。函数用 defun 定义:
(defun FUNCTION-NAME (ARGUMENTS...) "OPTIONAL-DOCUMENTATION..." (interactive ARGUMENT-PASSING-INFO) ; optional BODY...)
- 函数文档是函数的功能描述,可选。我们在 C-h f 查询函数的时候查询到的就是这个函数的文档描述。
- 交互选项,可选。我们在 Emacs 中使用 M-x 执行的函数,都是交互的。
我们定义一个简单的加法函数, C 里是这样的:
int add (int a, int b) { return a + b; }
我们用 elisp 是这个样子:
(defun add (a b) "函数 add 用来计算参数的和" (+ a b))
在末尾按下 C-x C-e, 然后我们的函数就算安装完成了。C-h f add 我们就看到我们的函数文档了。执行一个函数:
(add 12 13) => 25
函数作为符号,设置用 fset:
(fset 'abc 'add) (abc 12 13) => 25
8 实例
8.1 判断闰年
闰年的定义如下:
- 对非世纪年份(不能被100整除),能被4整除的为闰年
- 对世纪年份(能被100整除的),能被400整除的为闰年
(defun leap-year-p (year) "判断年份是否为闰年" (if (= 0 (mod year 100)) ;; 是否是世纪年份 (= 0 (mod year 400)) ;; 世纪年份被 400 整除 (= 0 (mod year 4)))) ;; 非世纪年份被4整除
同 0 比较如此常见,有了另一个方便的谓词: zerop, 一个 zerop的版本:
(defun leap-year-p (year) "判断年份是否为闰年" (if (zerop (mod year 100)) ;; 是否是世纪年份 (zerop (mod year 400)) ;; 世纪年份被 400 整除 (zerop (mod year 4)))) ;; 非世纪年份被4整除 (leap-year-p 1900) => nil (leap-year-p 1904) => t
8.2 斐波那契数列
斐波那契数列的递归定义如下:
- F0 = 0
- F1 = 1
- Fn = Fn-1 + Fn-2
求数列的第n项程序:
(defun fib-nth (n) "求斐波那契数列的第n项值" (cond ((= n 0) 0) ((= n 1) 1) (t (+ (fib-nth (- n 1)) (fib-nth (- n 2)))))) (fib-nth 10) => 55
2022年8月26日 13:06
MyIWU is our faculty, staff and student portal. Access the MyIWU Portal. What can I do or find here? Access other applications like Banner Self Service, indwes edu login Gmail 2021. Indiana Wesleyan University Login | IWU Student login Online MyIWU Portal 2021 at IWU Portal https://myiwu.indwes.edu/. It enables online learning for the students and has bridged the gap between the student and teacher. You can sign in to your account.MyIWU is our faculty, staff and student portal. Access the MyIWU Portal. What can I do or find here? Access other applications like Banner Self Service.
2024年1月18日 01:22
Unlock the world of soothing massages with 펀초이스. Let reviews guide you to serenity and rejuvenation.
2024年1月18日 05:58
Immerse yourself in the world of 제주룸싸롱 – a haven of opulence and entertainment in Jeju Island.