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

Lisp基础

Lisp是仅次于Fortran的第二古老的著名计算机语言。
Lisp从一开始就与众不同的一点在于,它是基于S-表达式的语言。也就是说,代码和数据是用同一种方式表达出来的。
S-表达式,我们直观上理解,就是用括号括起来的一串列表。
比如:

(+ 1 1)

Lisp会对这个S-表达式进行求值。

S-表达式可以嵌套,比如可以这样写

(+ 1 (* 2 3))

在lisp中,默认的操作是对S-表达式求值。如果是个数字,就对数字直接求值。如果是字符串也是如此。如果是个表,则将第一个原子当成函数名,对其进行求值。

那么,如果我们只想将一个S-表达式当成数据,不要计算怎么办?我们可以使用quote符号来进行指定,比如:

(quote (+ 1 2))

结果就返回(+ 1 2)这个列表。
quote在lisp中实在是太常用了,所以我们可以用符号“’”来作为它的简称。

定义变量

大家都是写代码的老司机啦,我们就直接从定义变量开始说起。

定义变量可以使用set函数。

(set 'three (+ 1 2))

要注意,因为变量名是不求值的,所以要用quote或者'来阻止求值。将来用变量的值的时候就不用quote了。

定义变量的时候,符号是基本上不可能用求值的,所以'是基本上必须的。每次写太麻烦,于是专门有个setq特殊表,直接包含了对于第一个参数的不求值的quote行为。

定义带文档的变量:defvar特殊表

setq特殊表几乎是lisp语言中最常用的特殊表,但是它有一个问题,变量没有文档。而在emacs中,函数和变量的文档是非常受到重视的。
对于代码中有重要作用的变量,在定义的时候,我们可以通过defvar特殊表来实现。
defvar特殊表不同于setq的是,它只针对未赋过初值的新变量有效,如果已经有值了,它就不起任何作用。所以我们在第一次定义变量的时候使用defvar特殊表吧,文档还是很重要的。

定义局部变量:let特殊表

let可以定义局部变量。let定义的绑定只在函数内部起作用。

格式:

(let ((变量名 绑定值)(变量名 绑定值)) 语句)

例:

(let ((a 1)(b 2)) (+ a b))

a被赋值1,b被赋值2.最终表达式结果是3.

在let中,如果未指明绑定值,则自动绑定到nil上。

注释

emacs的注释以分号开头,分号之后全是注释。

表处理

car和cdr:取表头和其余部分

下面我们开始进入lisp不同于命令式语言的一个特色功能,表处理。

  • car函数:取一个表的第一个元素
  • cdr函数:取一个表的除了car取到部分的其它部分

例:

(car '(1 2 3))

将返回1.

(cdr '(1 2 3 4 5))

取得的结果是(2 3 4 5)

nthcdr函数:多次cdr

如果我们要从(1 2 3 4 5)这个表中取(3 4 5)这样的子表,一次cdr不够,需要做两次,像这样:

(cdr (cdr '(1 2 3 4 5)))

有个更简单的写法是使用nthcdr函数:

(nthcdr 2 '(1 2 3 4 5))

nthcdr的第一个参数,如果是0,则直接返回原表。如果是1,则退化成cdr。

cons函数:将car和cdr拼接起来

cons是car和cdr的逆运算。将两个表拼接成一个新表。
例:

(cons '1 '(2 3 4))

将得到(1 2 3 4).
请注意,cons会将第一个参数当成一个元素处理。
比如:

(cons '(1 2 3) '(4 5 6))

得到的结果不是(1 2 3 4 5 6)而是((1 2 3) 4 5 6)

append函数:将两个表合成一个表

cons是将car和cdr合在一起,如果是想将(1 2 3)和(4 5 6)连接成(1 2 3 4 5 6),此时应该使用append函数:

(append '(1 2 3) '(4 5 6))

获取表中最后一个元素:last函数

car可以获取第一个元素,获取最后一个元素的话可以使用last函数。

构造一个新表: list函数

(list 1 2 3 4)

将构造出(1 2 3 4)表。

求表长度:length函数

例:

(length '(1 2 4 5))

结果为4.

给表换car和cdr:setcar和setcdr函数

例:

(setq list1 '(1 2 3 4))
(setcar list1 5)

此时再通过C-h v去查list1的值,已经变为(5 2 3 4).

我们再将其后部也换掉:

(setcdr list1 '(6))

list1此时的值已经变成(5 6)

将表逆序排列:reverse函数

例:

(reverse '(1 2 3 4))

结果为:(4 3 2 1)

像命令语言一样顺序编程

Lisp基本上是一种函数式的语言。也就是说,如果在C语言中这样写的语句:

int a=func1(0);
int b=func2(a);
int c=func3(b);

换成lisp的写法是这样的:

(func3 (func2 (func1 0)));

我们来个小例子看看:

(defun func1 (x)
  (+ x 1))
(defun func2 (x)
  (* x 2))
(defun func3(x)
  (* x x))
(func3 (func2 (func1 0)))

先执行的,要写在最里面。但是,学习命令式编程的同学们,习惯于先写最先执行的func1,倒过来写觉得思路转不过来。

Lisp善解人意地提供了progn特殊表,可以像C一样顺序执行。
我们可以这么写:

(progn
  (setq a (func1 0))
  (setq b (func2 a))
  (setq c (func3 b)))

针对整个表的进行操作 - mapcar函数

Lisp毕竟是针对表进行处理的语言,如果像命令式的方式处理一个表未免有点low了。

假如我们想将一个表中的每个数字都求平方,我们先定义一个平方函数:

(defun sqr2 (x)
  (* x x))

然后,我们就可以调用mapcar函数,对整个表都应用sqr2函数:

(mapcar 'sqr2 '(1 2 3 4))

针对这样只用一次的函数,我们可以使用lambda表达式来实现:

(mapcar (lambda (x) (* x x)) '(5 6 7 8))

lambda很有趣,既不是函数,也不是特殊表,而是一个宏。关于宏,我们后面再讲。

apply函数

最后介绍一下apply函数,它是可以将函数对表执行操作的函数。
我们看个例子:

(apply '+ '(5 6 7 8))

就相当于:

(+ 5 6 7 8)

小结

  • setq特殊表:定义变量
  • defvar特殊表:定义带有文档的变量,只能做第一次的初始化,有值就不能用了
  • let特殊表:定义函数内部的局部变量
  • car和cdr,nthcdr:取表头和其余部分
  • last函数:取表尾
  • cons: 构造新表
  • length函数:求表长度
  • append函数:将几个表合并为一个
  • reverse函数:将表反序重排
  • setcar, setcdr: 给表换头或换其余部分
  • mapcar函数:针对每个元素分别进行操作
  • apply函数:针对表执行操作
  • lambda宏:定义匿名函数
  • progn特殊表:顺序编程
时间: 2024-10-30 13:04:53

我也说说Emacs吧(7) - lisp基础的相关文章

我也说说Emacs吧(6) - Lisp速成

前面我们学习了基本操作,也走马观花地看了不少emacs lisp的代码.这一章我们做一个lisp的速成讲座. Lisp的含义是表处理语言.它的代码组成结构都是用括号组成的表来表示的.Lisp中的功能,要么是以函数形式求值,要么本身就是一些特殊表. 比如在Lisp语言中,判断分支的if不是语句,也不是函数,而是一种特殊的表.定义函数的方式,也是用一种叫做defun的特殊表. Lisp基本函数速成 首先我们搭建一下环境,随便建一个.el为扩展名的文件.然后,我们写一个helloworld的代码吧:

使用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-

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

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

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

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

【转】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.它们以集成方式提供给最终

[译]跌宕起伏的函数式编程(软件编写)(第一部分)

本文讲的是[译]跌宕起伏的函数式编程(软件编写)(第一部分), 烟雾的方块艺术 -MattysFlicks -(CC BY 2.0) 注意:这是从基础学习函数式编程和使用 JavaScript ES6+ 编写软件的第一部分.保持关注,接下来还有很多! 当我 6 岁时,我花了很多时间跟我的小伙伴玩电脑游戏,他家有一个装满电脑的房间.对于我说,它们有不可抗拒的魔力.我花了很多时间探索所有的游戏.一天我问他,"我们怎样做一个游戏?" 他不知道,所以我们问了他的老爸,他的老爸爬上一个很高的架子

下一代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 代码: 原生

探索JVM上的LISP

当前Java领域最激动人心的事情莫过于可允许其它编程语言运行于Java虚拟机上.围绕JRuby.Groovy.Scala还有 Rhino(JavaScript引擎)的讨论已经甚嚣尘上.可为什么要墨守陈规呢?如果你真的想跳出主流,投身于一种与Java截然不同的的语言,Lisp就不失为一种很好的选择.现在已有几种可运行于JVM上的Lisp程序设计语言的开源实现,准备好开始我们的探索之旅吧! Lisp有什么值得研究呢?首先,作为已有50年历史的语言,它促成许多被我们今日视为理所当然的观念.if-the