我也说说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特殊表:用来定义函数

有了上面的知识,分支、子程序结构都有了,我们可以像写命令式语言一样写代码了。

时间: 2024-09-16 16:06:36

我也说说Emacs吧(6) - Lisp速成的相关文章

我也说说Emacs吧(7) - lisp基础

Lisp基础 Lisp是仅次于Fortran的第二古老的著名计算机语言. Lisp从一开始就与众不同的一点在于,它是基于S-表达式的语言.也就是说,代码和数据是用同一种方式表达出来的. S-表达式,我们直观上理解,就是用括号括起来的一串列表. 比如: (+ 1 1) Lisp会对这个S-表达式进行求值. S-表达式可以嵌套,比如可以这样写 (+ 1 (* 2 3)) 在lisp中,默认的操作是对S-表达式求值.如果是个数字,就对数字直接求值.如果是字符串也是如此.如果是个表,则将第一个原子当成函

使用Emacs:生存篇

vim和Emacs都是很强大的编辑器.所以,入门有一定难度.这里不谈vim,谈Emacs下的生存--第一次使用Emacs时的使用. 1.emacs的安装: 在Fedora下: sudo yum install emacs 2.打开emacs: 终端中输入emacs 3.emacs的界面: 当你打开emac后应该是这样子: 看不懂英文?"快速指南"一行对应中文手册可以查看.最基本的按键: C-x C-f 打开或创建文件 C-x s保存 C-x b 'buffername'切换缓冲区 C-

【转】linux环境下的c++编程

linux环境下的c++编程        就C++开发工具而言,与Windows下微软(VC, VS2005等)一统天下相比,Linux/Unix下C++开发,可谓五花八门,各式各样.Emacs, vi, eclipse, anjuta,kdevelop等层出不穷.         Windows下,开发工具多以集成开发环境IDE的形式展现给最终用户.例如,VS2005集成了编辑器,宏汇编ml,C /C++编译器cl,资源编译器rc,调试器,文档生成工具, nmake.它们以集成方式提供给最终

热点技术:编辑器背后的程序观

最近看到新闻,Eclipse 的市场份额持续下滑,而 Intellij IDEA 异军突起终于坚持不懈的超越了 Eclipse 成为了 Java 程序员的首选开发工具. 有感于此,回想起这么些年来写过不同的语言,用过不少的编辑器.以前的程序员和现在的程序员他们都各用什么编辑器编程,不同语言的程序员偏爱什么样的编辑器来写程序呢? 编辑器与语言 先来看看下面这张图,来自 TIBOE 2016 十月最新的编程语言流行度排行. Java 以后端和安卓开发为主,牢牢占据第一.Java 的历史不过短短二十年

从Hello World到defmacro,那些令人惊叹的代码!

前言 自从看到那个征文活动便灵感突现,这是个为大家介绍Lisp语言的机会,也是个赞扬最让我心动的语言的机会. 毕竟还是学生党,还未有太多时间来学习它,但内心满满的都是热爱与兴奋.文中如有疏漏,还请各位指教! 一次偶然在<黑客与画家>第二版中了解到这门神奇的语言,瞬间便被"洗脑",立刻找到一大堆资料,前前后后的兴奋的学了几个月,无奈于就业压力,还是选择先将C++/Java等作为主力. 这篇文章主要面向没见过Lisp语言的同学,否则就会觉得这些太简单了,Lisp的博大精深也不是

下一代Eclipse 步入云端_java

带着大家一起了解"下一代Eclipse 步入云端" 一.安装方法 注意:Che目前是pre-alpha的版本,请从源代码编译来体验workspace/environment概念. Che需要Docker,可以根据需要查看Windows或者MacOS安装Docker的有关信息. 安装方法: git clone git checkout 4.0 cd assembly-sdk mvn clean install cd .. ./che run http://localhost:8080 二

用 Emacs Lisp 开发 CGI 程序

Emacs Lisp 作为编程语言也是非常强大的.尤其 Emacs 作为一款编辑器,自带了很多处理文本的函数,用起来很方便. 我一直希望用 Emacs Lisp 作为服务端脚本语言来开发 Web 程序.在网上搜索了很久,还真有人做过类似的事情:http://www.emacswiki.org/emacs/cgi.el.但他封装的还不够彻底,用起来还是挺麻烦,于是自己动手写了一个.目前支持: script-let,即可在 <% %> 或 <%= %> 之中插入 lisp 代码: 原生

Emacs之魂(一):开篇

Emacs之魂(一):开篇Emacs之魂(二):一分钟学会人界用法Emacs之魂(三):列表,引用和求值策略Emacs之魂(四):标识符,符号和变量Emacs之魂(五):变量的"指针"语义Emacs之魂(六):宏与元编程Emacs之魂(七):变量捕获与卫生宏Emacs之魂(八):反引用与嵌套反引用Emacs之魂(九):读取器宏 程序员大部分的时间都是在和代码打交道,因此,对于文本编辑器一定不会陌生了. 编辑器是处理文本的工具. 就像趁手的兵器对武林高手的辅助作用一样, 强大的编辑器也会

Emacs之魂(四):标识符,符号和变量

Emacs之魂(一):开篇Emacs之魂(二):一分钟学会人界用法Emacs之魂(三):列表,引用和求值策略Emacs之魂(四):标识符,符号和变量Emacs之魂(五):变量的"指针"语义Emacs之魂(六):宏与元编程Emacs之魂(七):变量捕获与卫生宏Emacs之魂(八):反引用与嵌套反引用Emacs之魂(九):读取器宏 1. 符号 上文我们提到了Emacs Lisp是一种Lisp-2, 即同一个符号(symbol)在不同的上下文中,可以分别表示两种不同的值(value): 变量