emacs lisp基本

Table of Contents

1. 变量声明与函数定义

1.1. 通过setq声明变量

setq是声明变量的关键词,用法如下:

(setq foo "bushiwo")
(message foo)

输出结果是:

"bushiwo"

message函数起到了将变量输出到缓冲区的作用.

1.2. 通过defvar定义不会改变声明的变量

用法和setq一致,但是定义的变量数值不会发生改变.如果定义的变量已经被定义,那么赋值将会失败. 比如说:

;; 在前面已经通过setq定义了foo的前提下
(defvar foo "liangzi"
  "This value will say who is a foo.") ;; 这一行是对document进行解释.

当我们使用message对其进行打印时,其输出结果同样是"bushiwo".

1.3. 通过defun定义函数.

比如,我想定义一个求圆的面积的函数,我可以这么去写:

;; 首先,声明PI的值
(setq pi 3.14)
;;;3.14

(defun get-circle-sq (rr)
  "这是一个求圆的面积的函数" ;;这是一行注释,表明这个函数在干什么
  (message "圆的面积是%.2f" (* pi (* rr rr)))
  (* pi (* rr rr))
  )
;;;get-circle-sq

(setq r 2)
;;;2

(get-circle-sq r)
;;;12.56

(setq cc (get-circle-sq r))
;;;12.56

1.4. 使用局部作用域

对于刚刚的函数,我们把pi定义在了函数外面,这样并不好.并且,在函数内部我们没有定义任何的局部变量.这在一个较长的函数里面是难以实现的.下面就介绍如何使用let进行局部作用域的定义.并在此基础上,考虑局部变量的声明问题.

(defun get-circle-sq (r)
  (let (
        (pi 3.1415926)
        sq
        )
    (setq sq (* pi (* r r)))
    (message "the sq of [%.2f]r is %.2f" r sq)
    )
  sq)


;;;get-circle-sq

(get-circle-sq 2)
;;;12.56

当然,也可以用let* 让表达式更加精简,比如:

(defun circle-area (radix)
  (let* ((pi 3.1415926)
         (area (* pi radix radix)))
    (message "直径为 %.2f 的圆面积是 %.2f" radix area)))

1.5. 在对函数和变量观察时的技巧(使用C-h)

我们可以通过C-h f来查询一个函数相关的信息,或者通过C-h v来擦汗寻一个变量的信息.

1.6. 匿名函数表达式lambda

lambda表达式的格式和寻常的函数定义比较相似(只不过它没有名字而已.) 下面是一个简单的例子:

(lambda (r)
  "find the sq of r"
  (* r r)
  )

在对函数进行调用时,可以简单地使用funcall. 下面是一个例子:

(funcall (lambda (r)
           (* r r)) 2)

或者把它赋值给一个变量:

(setq sq (lambda (r)
        (* r r)))

(funcall sq 2 )

除此之外, lambda主要被用于作为参数在函数之间进行传递.这也是函数式编程的主打卖点吧.

2. 基本逻辑顺序

下面对每门语言基本存在的逻辑顺序进行简单的介绍.

2.1. 顺序执行

顺序执行在函数里是默认的.这毫无疑问,也毫无问题.显式地确定顺序执行的方法是使用progn,如:

(progn
  (setq a 3)
  (setq b 4)
  (message "b-a=%d" (- b a)))

;;;b-a=1

2.2. if 与cond

if大家都比较熟悉,cond就是类似于switch-case的结构.下面以一个例子作为介绍:

(defun myabs (x)
  (if (> x 0)
      x
    (* -1 x)))

(myabs -1)
;;;1

(defun good-abs(x)
  (cond ((> x 0) x)
        ((< x 0) (- 0 x))
        ((= x 0) 0)
        ))

(good-abs -3)
;;;3

2.3. while 控制循环

事实证明,这个while循环很危险,经历一次死机之后,此处的所有文档全部消失了.

(defun factorial (n)
  (let ((res 1))
    (while (> n 1)
      (setq res (* res n)
            n (- n 1)))
    res))
(factorial 10)

2.4. 逻辑运算 and or not

不消多说,直接上例子:

(defun get-rectangle-sq (a &optional b)
  (or b (setq b a))

  (if (= a b)
      (progn
        (message "这是一个正方形")
        (* a a)
        )
    (progn
      (* a b))
      )
  )

(get-rectangle-sq 2)
;;;4
(get-rectangle-sq 2 3)
;;;6

3. 数据结构

3.1. 数字

下面简单列一下数字相关的函数,详情可以来这里学习.

;; 测试函数
(integerp OBJECT)
(floatp OBJECT)
(numberp OBJECT)
(zerop NUMBER)
(wholenump OBJECT)
;; 比较函数
(> NUM1 NUM2)
(< NUM1 NUM2)
(>= NUM1 NUM2)
(<= NUM1 NUM2)
(= NUM1 NUM2)
(eql OBJ1 OBJ2)
(/= NUM1 NUM2)
;; 转换函数
(float ARG)
(truncate ARG &optional DIVISOR)
(floor ARG &optional DIVISOR)
(ceiling ARG &optional DIVISOR)
(round ARG &optional DIVISOR)
;; 运算
(+ &rest NUMBERS-OR-MARKERS)
(- &optional NUMBER-OR-MARKER &rest MORE-NUMBERS-OR-MARKERS)
(* &rest NUMBERS-OR-MARKERS)
(/ DIVIDEND DIVISOR &rest DIVISORS)
(1+ NUMBER)
(1- NUMBER)
(abs ARG)
(% X Y)
(mod X Y)
(sin ARG)
(cos ARG)
(tan ARG)
(asin ARG)
(acos ARG)
(atan Y &optional X)
(sqrt ARG)
(exp ARG)
(expt ARG1 ARG2)
(log ARG &optional BASE)
(log10 ARG)
(logb ARG)
;; 随机数
(random &optional N)

3.2. 字符与字符串

3.2.1. 字符的表示

elisp里面的字符和C一样,都是用单引号表示,同时与一个整型数字挂钩(比如ASCII值). 此处给出一个简单的示例:

;; 'A'
?A ;;-> 65

除此之外, 对于一些自身包含意义的符号, 可以使用转义字符进行表达. 比如, 对于( 可以用?\(进行表达.

除此之外引申出来的就是一些控制字符, 如:

?\a => 7                 ; control-g, `C-g'
?\b => 8                 ; backspace, <BS>, `C-h'
?\t => 9                 ; tab, <TAB>, `C-i'
?\n => 10                ; newline, `C-j'
?\v => 11                ; vertical tab, `C-k'
?\f => 12                ; formfeed character, `C-l'
?\r => 13                ; carriage return, <RET>, `C-m'
?\e => 27                ; escape character, <ESC>, `C-['
?\s => 32                ; space character, <SPC>
?\\ => 92                ; backslash character, `\'
?\d => 127               ; delete character, <DEL>

另一种更寻常的控制字符的生成格式是:

?\^I ?\^i ?\C-I ?\C-i

类比之,Meta键的控制为?\M-\C-b这种风格.

3.2.2. 由字符产生字符串的一些函数

这部分的内容还挺无聊的,总结一下就是:

  1. 由重复的字符构造字符串 make-string
  2. 连接若干个字符构成字符串 string
  3. 从一个字符串中按照索引提取一部分字符 substring
  4. 字符转成字符串 char-to-string
  5. 整型数字转成字符串 number-to-string
  6. 连接字符形成字符串 concat
  7. 全部小写化 downcase
  8. 全部大写化 upcase
  9. 首字母大写 capitalize, upcase-initials

3.2.3. 对字符串的简单处理,查找和替换

  1. 查找

    下面是进行查找的一个示例:

    (string-match "34" "01234567890123456789")    ; => 3
    (string-match "34" "01234567890123456789" 10) ; => 13
    

    在这个例子中,"34"作为一个被匹配的字符串,函数将会返回该字符串匹配的第一个索引,当然,也可以使用数字来确定从哪里开始搜索,比如下面的那行所确定. 实际上,string-match所接受的被查找参数是可以出现字符串的.

    在此函数的基础上,可以构造出一个连续查找的函数,如下所示:

    (let ((start 0))
      (while (string-match "34" "01234567890123456789" start)
        (princ (format "find at %d\n" (match-beginning 0)))
        (setq start (match-end 0))))
    
    

    其中,match-beginning与match-end都是常见的两个东西.

  2. 替换

    替换的功能需要稍微依据查找来实现,比如:

    (let ((str "01234567890123456789"))
      (string-match "34" str)
      (princ (replace-match "x" nil nil str 0))
      (princ "\n")
      (princ str))
    
    

    其中,replace-match函数实现了复现的效果.

3.3. cons cell and list

3.3.1. cons cell

cons cell 就是一个二元组. 这个二元组主要可以分成两部分,(CAR CDR). 一般而言, 初始化一个cons cell的基本语法是:

'(1 . 2)                                ; => (1 . 2)
'(?a . 1)                               ; => (97 . 1)
'(1 . "a")                              ; => (1 . "a")
'(1 . nil)                              ; => (1)
'(nil . nil)                            ; => (nil)

可以看出,任何不同类型的两个东西都可以通过这种方式汇聚在一起. 所以说, cons cell本质上是通过一种指针式的索引构造出来的. 这也是cell的由来.

有一个问题,就是括号前面的单引号. 我们为什么要在括号前面放一个单引号呢? 是这样的, 我们知道在elisp的语法里括号里面的第一项都是一个可以被执行的函数, 而我们构造的cons cell 显然不是这种情况. 所以, 我们就使用一个单引号来将之符号化, 所谓的符号化就是指的"引用化", 也就是暂时不对其进行解析. 这个单引号的作用近似于(quote (1 . 2)).

通过这样的定义,我们对一个cons cell的输出显然也就是其输入了.

3.3.2. list

list常常是指由cons cell生成的一些东西. 我们来看一些特殊的list吧.

  • 空列表. 就是nil,或者'(). 显然空列表不是cons cell,因为没有CAR和CDR. 但是我们如果用CAR与CDR对其进行访问,都可以得到nil,所以我们认为它是conscell未尝不可.
  • true list. 对一个list无数求cdr,最后得到的是nil.
  • dotted list. 求cdr的结果是一个非cons cell的东西.
  • circular list. 环形列表.

参考链接


Author: 梁子 (frostliangzi@qq.com) Create Date: 2020-07-07 Tue 00:00 Last modified: 2024-03-09 Sat 20:56 Creator: Emacs 28.1 (Org mode 9.5.2)