Emacs折腾日记(四)——elisp控制结构

news/2024/12/18 11:21:52/

目前我们接着学习elisp相关语法,这里我是按照 elisp 简明教程 来进行学习。与其说这是我自己写得教程到不如说是在这个上面做得注释。目前我不知道这样是否侵犯相关的知识产权。目前就先这样继续学习,继续写记录吧。

闲话少说,进入本篇的正题,关于elisp的控制结构。一般编程语言都有三种控制结构:顺序结构、条件结构、循环结构。elisp同样有这三种控制结构。

顺序结构和复合语句

一般默认elisp的语句是顺序执行的,例如下面的代码

emacs-lisp">(setq name "Emacs")
(message "hello, %s" name)

它先执行前面的 setq 语句,先给变量name 定义并赋值为 Emacs 。后面接着执行第二行代码,调用message 函数来输出一段文字。

在其他语言一般都有一个复合语句。它是有多个语句共同组成的,例如 C/C++中使用{} 来将多个语句整合成一条复合语句。针对C/C++ 我们在很多地方会用到复合语句。例如如果 if , while 等语句后只需要一条语句,那么可以直接使用一条语句,例如下面的代码

// 这么写代码不太正规但是符合语法规范,也能编译过
int main()
{int i = 0;while(i++ < 10)printf("%d\n", i); //打印1到10,这么10个数字return(0);
}

但是如果在循环或者if条件成立后,执行多条语句,就需要使用复合语句,也就是用大括号括起来。

那么在elisp中也有这样的操作,在条件和循环语句中需要执行不止一条语句,也需要使用复合语句。

elisp 中符合语句使用 progn 来包含一组语句组成复合语句,它的语法规则是

emacs-lisp">(prognstatement1statement2...statement3)

例如我们将上面的代码用 progn 包装一下

emacs-lisp">(progn(setq name "Emacs")(message "hello, %s" name)) ;; => "hello, Emacs"

使用 progn 包装的复核语句可以使用 C-x C-e 也就是 eval-last-sexp 来同时执行里面的两个子语句。如果我们将它们分开写,则使用 eval-last-sexp 做不到这点,它只能一条条的执行

条件语句

我们使用 ifcond来表示条件分支,if的语法如下

emacs-lisp">(if conditionthen
else)

需要注意的是 这里的 thenelse 并不是关键字,而是对应的语句,也就说紧跟着if条件的语句表示条件成立时执行的代码,下一条则是条件不成立时执行的代码。例如我们使用下面的代码来获取两个数的最大值

emacs-lisp">(defun get-max(a b)(if (> a b)ab))(get-max 3 4) ; => 4

与 C/C++ 的函数不同,elisp 函数的返回值不需要使用 return 或者其他的关键字特意指出,它是将函数最后执行的语句的返回值作为函数的返回值,这里当 a > b 时条件成立,执行 a 然后结束函数,也就是这个时候函数的最后一个语句是 a ,函数返回 a 的值。否则执行 b ,此时函数的最后执行的语句就是 b ,这个时候函数就返回 b 的值

cond 有点像 C/C++ 中的 switch ,它的语法如下

emacs-lisp">(cond (case1 do-when-case1)(case2 do-when-case2)...(t do-when-none-meet))

它的语法特点是,它与 switch 类似,由一堆 casedefault 组成。每个case 都使用一对 () 来区分,最后可以使用 t 来表示未匹配到前面的 case 时执行的语句,类似于default语句。这里我们使用当初学习C/C++ switch 语法时的经典代码来作为示例

emacs-lisp">(defun score-report (score)(cond ((>= score 90) "优秀")((>= score 80) "良好")((>= score 60) "及格")(t "不及格")))(score-report 75); => 及格

我们可以看到,cond 语句的使用比 switch 更为的灵活,switch case 只能进行整型变量的相等比较,而 cond 可以进行其他变量类型的不同形式的条件判断,它只是在形式上更像 switch,但是在使用的范围上更像 if-else if-else。另外 elisp 简明教程中 提供了一个使用 cond 计算 斐波那契数列的例子

emacs-lisp">(defun fib(n)(cond ((= n 0) 0)((= n 1) 1)(t (+ (fib (- n 1)) (fib (- n 2))))))
(fib 10) ; => 55

因为 elisp 中使用 setq 来进行赋值操作,所以它里面的= 就是数学意义上比较相等的操作符,而 其他语言中的 == 在lisp中无效。这里如果写成 == 将会报错。

上面的例子也很好理解 当 n 等于 0时返回0,等于 1 时返回1,否则返回 fib(n - 1) + fib(n - 2) 使用 C/C++ 的话可能更容易理解

int fib(int n)
{if(i == 0)return 0;else if (i == 1)return 1;elsereturn fib(n - 1) + fib (n - 2)
}

循环结构

循环使用 while 关键字,它的语法结构如下

emacs-lisp">(while conditionbody)

我们可以将上述循环打印的C代码使用 elisp 实现

emacs-lisp">(setq i 0)
(while (< i 10)(progn (message "%d" i)(setq i (+ i 1))))

我们执行完代码之后使用 switch-buffer,切换到 *message* ,可以看到它打印了从0到9的数据。上面的斐波那契数列的例子我们可以使用 while 来实现

emacs-lisp">(defun fib (n)(cond ((= n 0) 0)((= n 1) 1)(t (let ((first 1)(second 1)(third 1))(setq n (- n 2))(while (> n 0)(progn(setq third (+ first second))(setq first second)(setq second third)(setq n (- n 1))))third))))(fib 10) ; => 55

因为 elisp 中没有提供 += ++ 这样算术运算符,所以我们需要使用 setq 来赋值。

下面还有一个计算阶乘的例子

emacs-lisp">(defun factorial (n)(let ((res 1))(while (> n 1)(setq res (* res n))(setq n (- n 1)))res))(factorial 10) ; => 3628800

我们也可以提供一个递归的版本

emacs-lisp">(defun factorial (n)(if (= n 1)1(* (factorial (- n 1)) n)))(factorial 10) ; => 3628800

到此为止,本篇就结束了。本篇涉及到的elisp 代码其实也不算复杂,如果能熟练掌握一门编程语言的话,到此为止的代码应该不算太难理解。在编写这些示例代码的时候我觉得还好,主要注意括号的匹配,算法什么的就是照搬C/C++中一些经典写法就差不多了。但是即使上面的代码并不多,代码量并不大,我也能明显感觉到上述代码在阅读上不那么直观。


http://www.ppmy.cn/news/1556107.html

相关文章

Linux高性能服务器编程 | 读书笔记 | 10. 高性能I/O框架库Libevent

10. 高性能I/O框架库Libevent Linux服务器程序必须处理三类事件&#xff08;I/O、信号和定时事件&#xff09;&#xff0c;在处理这三类事件时需要考虑以下问题&#xff1a; **统一事件源。**统一处理这三类事件既能使代码简单易懂&#xff0c;又能避免一些潜在的逻辑错误。实…

基于51单片机的键盘/键值/矩阵键盘/数码管proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1aR7CrNG5lmftQ1MtW05LKw 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectro…

机器学习03-内部学习分享

机器学习03-内部学习分享 文章目录 机器学习03-内部学习分享[toc]1-机器学习概念区分1-传统机器学习VS深度学习-12-传统机器学习VS深度学习-23-传统机器学习VS大模型-3 2-神经网络的演进1-基础数学发展2-神经网络概念提出3-CPU的架构设计、量子计算机的架构设计以及神经元的计算…

令牌(token)+加密(加盐)

目录 一,令牌技术 1,不使用session的原因: 2,有两种解决方案: (1)服务器层面的 (2)客户端层面的(JWT令牌) 生成签名: 生成jwt令牌: 验证令牌是否合法: (3)令牌实际运用 二,加密加盐: 进行加密: 进行验证: 一,令牌技术 1,不使用session的原因: 登录页面,用户会将密…

在OpenCV中轮廓处理

在OpenCV中轮廓处理 函数主要包括以下几个&#xff1a; 阈值化&#xff1a;将图像转换为二值图像&#xff0c;以便更容易地检测轮廓。形态学操作&#xff1a;使用形态学操作&#xff08;如开运算&#xff09;来去除噪声。边缘检测&#xff1a;使用Canny边缘检测算法来检测图像…

day12-历史排行榜

1. 数据存储方案 1.1 分区 1.1.1 优点 &#xff08;1&#xff09;水平拆分&#xff0c;逻辑上还是一张表&#xff0c;业务中的sql语句不需要改变 &#xff08;2&#xff09;提高数据检索、统计的性能 &#xff08;3&#xff09;打破磁盘容量限制&#xff0c;不同的分区可以…

Java语言程序设计进阶篇_编程练习题19.2(使用继承实现GenericStack)

目录 题目 &#xff1a; 19.2&#xff08;使用继承实现GenericStack&#xff09; 代码示例 编程练习题19_2.java 编程练习题19_2Test.java 输出结果&#xff1a; 题目 &#xff1a; 19.2&#xff08;使用继承实现GenericStack&#xff09; /*19.2 * 程序清单19-1中&#…

如何使用通义千问 AI 生成 PPT 并发布到个人网站

使用通义千问 AI 快速生成 PPT&#xff0c;并通过 Canva 调整后嵌入 Hugo 网站&#xff0c;展示高效方法。 阅读原文请转到&#xff1a;https://jimmysong.io/blog/ai-ppt-to-hugo/ 以下是使用通义千问 AI 快速生成 PPT 并将其发布到个人网站的简单流程&#xff1a; 使用通义千…