我也说说Emacs吧(6) - Lisp速成
前面我们学习了基本操作,也走马观花地看了不少emacs lisp的代码。这一章我们做一个lisp的速成讲座。
Lisp的含义是表处理语言。它的代码组成结构都是用括号组成的表来表示的。Lisp中的功能,要么是以函数形式求值,要么本身就是一些特殊表。
比如在Lisp语言中,判断分支的if不是语句,也不是函数,而是一种特殊的表。定义函数的方式,也是用一种叫做defun的特殊表。
Lisp基本函数速成
首先我们搭建一下环境,随便建一个.el为扩展名的文件。然后,我们写一个helloworld的代码吧:
(message (concat "Hello" "," "World" "!"))
我们把光标停留在这行上,然后执行C-x C-e,或者执行命令eval-last-sexp,结果就会在下边的状态栏上打印Hello,World.
下面函数的实验,我们就都采用这个方式来进行。
查找函数的帮助
将光标放到要查询的函数上,比如在message中,运行C-h f,就可以查询message函数的帮助文档。
算术函数
加法 +
例:
(message "%d" (+ 1 1))
减法 -
(message "%d" (- 1 1))
乘法 *
例:
(message "%d" (* 123456789 987654321))
注意,在emacs lisp中,数字是会溢出的。
例错误:
(message "%d" (* 123456789 3145926535897932384626433832795928841971))
会报下面的错:
debug(error (overflow-error "3145926535897932384626433832795928841971"))
除法 /
需要注意的是,在emacs lisp中,除法结果是整数:
(message "%d" (/ 128.1 3.0))
结果是42
求余 %
需要注意的是,如果不是整数求余的话,会报错的。
例:
(message "%d" (% 128.1 3.0))
会报下面的错:
Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p 128.1)
%(128.1 3.0)
(message "%d" (% 128.1 3.0))
eval((message "%d" (% 128.1 3.0)))
如果不知道integer-or-marker-p函数的含义,还是老办法,光标移到过去,然后运行C-h f(describe function)去查询它的文档。
加1 1+
例:
(message "%d" (1+ 1))
等价于
(message "%d" (+ 1 1))
减1 1-
与1+完全类似。
例:
(message "%d" (1- 1))
数学函数
求e的阶乘,exp函数
(message "%d" (exp 10))
结果是22026,请注意结果是整数。
求阶乘函数 expt
(message "%d" (expt 2 8))
结果是256,2的8次方。
求对数函数 log
如果给两个参数,那么就log a b,是求以b为底的a的对数。如果省略b,则默认值为e.
(message "%d" (log 256 2))
(message "%d" (log 100))
输出为8和4.
平方根函数 sqrt
(message "The square root of 128 is:%d" (sqrt 128))
输出为:
"The square root of 128 is:11"
求绝对值函数
(message "The absolute vaule of -1 is:%d" (abs -1))
输出为:
The absolute vaule of -1 is:1
三角函数
sin, cos, tan都是例行公事的函数,反函数是asin, acos, atan,
(message "%d" (sin 0))
(message "%d" (cos 0))
(message "%d" (tan 1))
(message "%d" (asin 1))
(message "%d" (acos 1))
逻辑运算
- 两数逻辑与 logand
- 两数逻辑或 logior
- 两数逻辑异或 logxor
- 两数逻辑非 lognot
例:
(message "%d" (logand 1 0))
(message "%d" (logior 1 0))
(message "%d" (logxor 1 0))
(message "%d" (lognot 0))
第一个1与0为0. 第二个1或0为1. 第三个1与0异或为1. 第四个0取非是-1.
逻辑运算特殊表
在实际编程中,用于与或非逻辑运算的是几个特殊表:
* 与 and: 对每个参数进行求值,直到遇上一个nil
* 或 or: 对每个参数进行求值,直到遇上一个非nil
* 非 not:not是nil别名。其实取反就是判断一个逻辑值是否为nil.
比较函数
- =: 等于
- >: 大于
- <: 小于
- >=: 大于或等于
- <=: 小于或等于
- /=: 不等于
- eq: 等于
例:
(/= (logand 1 0) (lognot 1))
判断函数
- atom: 判断是不是一个原子
- listp: 判断是不是一个列表
- null: 判断是不是空
- stringp: 判断是不是一个字符串
- characterp: 判断是不是一个字符
- symbolp: 判断是不是一个符号
- zerop: 判断是不是0.
- intergerp: 判断是不是整数
(atom ())
(listp ())
(null ())
(stringp "Hello")
(characterp "h")
(symbolp nil)
(numberp 1)
(zerop 0)
(integerp 1)
if特殊表
学习了若干判断函数,我们当然需要一个控制结构来使用它,这个控制结构就是if特殊表。if是个特殊表,而不是函数,当然对于我们初学使用来说,这个区别并不重要。
if特殊表的结构是(if 条件判断 THEN表 ELSE表 … )
如果判断为非nil,则执行THEN表,否则执行ELSE表。
我们看个例子:
(if (atom ()) (message "() is an atom") (message "() is not an atom"))
输出当然是:() is an atom
cond特殊表
当条件特别多时,if特殊表嵌套可能会导致控制结构比较乱。这时我们可以使用cond特殊表来解决,cond特殊表的结构为:(cond 表1 表2 …)
cond特殊表的会一直执行后面的表,直至遇到任何一个表的值为非nil为止。
每个cond中的表都是由两部分组成:判断条件和其他值。cond特殊表执行时,会首先检查判断条件是否nil,如果为nil则继续执行,否则就返回非nil表后面的值。
我们还是通过一个例子来学习:
(cond ((= x y) "x and y are the same")
((> x y) "x is greater than y")
((< x y) "x is less than y"))
定义函数 - defun特殊表
学习了这么多预定义的函数,我们是不是也跃跃欲试,打算写几个自己定义的函数了呢?
Emacs Lisp的函数定义要比Common Lisp多几样东西。因为我们前面讲了,emacs lisp的函数就是我们正常调用的命令,所以它要定义交互方式,要定义函数文档。不过好在这两部分都是可选的,完全不知道的情况下,仍然能写出可以正确执行的函数来。
emacs lisp的defun特殊表的格式如下:
(defun 函数名 (形参列表) “可选的函数文档” (interactive;可选的交再继续方式)
函数体)
我们先写个简版的,实现判断一个数是不是偶数的函数:
(defun evenp (x)
(if (= 0 (% x 2)) t
nil))
(evenp 2)
我们再将其加上描述文档,同时加上一个是不是整数的判断:
(defun evenp (x)
"Check if x is an even number or not"
(if (and (integerp x) (= 0 (% x 2))) t
nil))
到目前为止,虽然看起来用括号不太习惯,但是我们已经学会用defun特殊表定义函数,用if和cond特殊表来实现判断。好像也没什么复杂的嘛?
学习了之后再回头看我们之前贴过的代码,是不是一下子就好懂了很多呢?
比如这个set-mark-command,不就是几个cond特殊表的组合么:
(defun set-mark-command (arg)
...
(cond ((eq transient-mark-mode 'lambda)
(kill-local-variable 'transient-mark-mode))
((eq (car-safe transient-mark-mode) 'only)
(deactivate-mark)))
(cond
((and (consp arg) (> (prefix-numeric-value arg) 4))
(push-mark-command nil))
((not (eq this-command 'set-mark-command))
(if arg
(pop-to-mark-command)
(push-mark-command t)))
((and set-mark-command-repeat-pop
(eq last-command 'pop-global-mark)
(not arg))
(setq this-command 'pop-global-mark)
(pop-global-mark))
((or (and set-mark-command-repeat-pop
(eq last-command 'pop-to-mark-command))
arg)
(setq this-command 'pop-to-mark-command)
(pop-to-mark-command))
((eq last-command 'set-mark-command)
(if (region-active-p)
(progn
(deactivate-mark)
(message "Mark deactivated"))
(activate-mark)
(message "Mark activated")))
(t
(push-mark-command nil))))
遇到具体的函数,我们都可以通过describe-function函数去查看它的文档和代码。
我们先只求会用,有个感性的认识,之后基础回头再补。
小结
几个特殊表:
* if特殊表:用来进行判断
* cond特殊表:多分支判断
* and特殊表:求值直到遇见nil为止
* or特殊表:求值直到遇见非nil为止
* defun特殊表:用来定义函数
有了上面的知识,分支、子程序结构都有了,我们可以像写命令式语言一样写代码了。
我也说说Emacs吧(6) - Lisp速成的更多相关文章
- My Emacs For Common Lisp
My Emacs For Common Lisp My Emacs For Common Lisp
- emacs 下 common lisp 配置
安装 sbcl .emacs 加入 ;for lisp mode (add-to-list 'load-path "D:/kuaipan/.emacs.d/elpa/slime-201311 ...
- 我也说说Emacs吧(7) - lisp基础
lisp基础 Lisp是仅次于Fortran的第二古老的著名计算机语言. Lisp从一开始就与众不同的一点在于,它是基于S-表达式的语言.也就是说,代码和数据是用同一种方式表达出来的. S-表达式,我 ...
- emacs最简单入门,只要10分钟
macs最简单入门,只要10分钟 windwiny @2013 无聊的时候又看到鼓吹emacs的文章,以前也有几次想尝试,结果都是玩不到10分钟就退出删除了. 这次硬着头皮,打开几篇文章都看完 ...
- 学习Emacs系列教程
emacs最简单入门,只要10分钟 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 3 ...
- 【程序猿助手】Emacs,最强的编辑器,之间的不
wx_fmt=png" alt="" style="max-width:100%; height:auto!important"> 内容简单 ...
- 【程序员小助手】Emacs,最强编辑器,没有之一
内容简介 1.Emacs简介 2.Emacs三个平台的安装与配置 3.自动补全插件 4.小编的Emacs配置文件 5.常用快捷方式 6.和版本控制系统的配合(以SVN为例) [程序员小助手]系列 在这 ...
- 我的Emacs折腾经验谈(一) 一些给新人的建议
这几天都没有动力写mongodb的东西,我果然还是太懒了么~ 主要是没有一个系统的东西整理出来,加上我令人拙计的语言表达能力,这个坑只能慢慢再补了. 最近在折腾emacs这个东西,首先说我曾经算是个极 ...
- 使用Emacs:生存篇
使用Emacs:生存篇 vim和Emacs都是很强大的编辑器.所以,入门有一定难度.这里不谈vim,谈Emacs下的生存--第一次使用Emacs时的使用. 1.emacs的安装: 在Fedora下: ...
随机推荐
- linux 下各个头文件的作用[典]
linux 下各个头文件的作用 2.6.30.4的头文件的位置和2.6.25.8的不一样,除去内核源码下的include目录外, 在arch/arm/mach-s3c2410/和arch/arm/ ...
- SVN添加忽略目录
项目:Thinkphp 目录结构: Thinkphp |-- Common |-- Runtime |-- Home 忽略目标: Runtime 文件夹及下面所有文件 首先,需要忽略的目录必须没有加入 ...
- 【bzoj2734】集合选数(有点思维的状压dp)
题目传送门:bzoj2734 这题一个月前看的时候没什么头绪.现在一看,其实超简单. 我们对于每个在$ [1,n] $范围内的,没有因数2和3的数$ d $,将它的倍数$ 2^a 3^b d $一起处 ...
- MongoDB 副本集和C#交互,简单测试
MongoDB 副本集和C#交互,简单测试 primary节点宕机: 模拟primary节点宕机的情况,这时查看监控: 可以看到37018已经成了primary节点.主界面宕机导致了整个集群发生一次e ...
- hdu4310 - Hero - 简单的贪心
2017-08-26 15:25:22 writer:pprp 题意描述: • 1 VS n对战,回合制(你打他们一下,需要受到他们所有存活人的攻击)• 你的血量无上限,攻击力为1• 对手血量及攻击 ...
- linux修改系统时间时区
修改时间: date -s "2017-08-10 17:00:00" clock -w 修改时区: 方法一: ln -sf /usr/share/zoneinfo/Asia/Sh ...
- Python3 学习第十三弹: 模块学习五之pickle与json
对于python来说,这两个模块是十分实用的两个模块,以一种简单的方法用于储存数据实例. pickle模块 提供用来储存Python各种数据序列化存储 # 原来的cPickle已经在python3中与 ...
- CentOS 6.5 x86_64升级内核到最新版2.6.32-696.1.1.el6.x86_64
安装keepaliver时内核版本不一致,会导致后来的keepalive安装完成后不能正常提供服务 [root@gmxfjr-dev17 ha]# ls /usr/src/kernels/2.6.32 ...
- JAVA运行war包
set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m java %JAVA_OPTS% ...
- [JS]JavaScript判断操作系统版本
function detectOS() { var sUserAgent = navigator.userAgent; var isWin = (navigator.platform == " ...