Emacs Lisp

Haskell 倾向于组合(compose)函数来完成一个综合功能
Lisp 倾向于用一个函数完成它自身的目标, 且提供各种参数开关用于改变该函数的功能

M-x lisp-interaction-mode 进入Lisp交互模式(一些主模式本身就已经具备此模式)

C-j 计算光标位置之前的表达式, 在下一行输出结果
C-x C-e 计算光标位置之前的表达式(通常光标位置应为最后一个后括号), 在Emacs底部的minibuffer区输出结果

以上两种方法都只能运行一条表达式, 而非整个文件里的表达式.

通常的Emacs Lisp代码编写流程:

  1. 1.
    编写一个函数
  2. 2.
    通过 C-x C-e 安装此函数
  3. 3.
    测试和改进此函数的功能
  4. 4.
    循环1-3

(+ 1 2) 加法运算(Lisp使用前缀运算符)

; 赋值字符串John Wick到变量user-name
(set 'user-name "John Wick"); 极少使用
(setq user-name "John Wick"); 最常用, 与set的区别在于无需输入符号(symbol)前的单引号
; 同时赋值多个变量
(setq variable1 t; True
variable2 nil; False
variable3 '("A" "list" "of" "things")')
; 定义
(defun func ()
(message "Hello, %s" name))
(defun multiply-by-seven (number)
"Multiply NUMBER by seven."
(* 7 number))
; 执行
(func "BlackGlory")

(defun hello () (insert "Hello, I am " my-name)) 将上述内容定义为一个名为hello的函数
(hello) 调用hello函数
(defun hello (name) (insert "Hello " name)) 有名为name的参数的hello函数
(hello "BlackGlory") 调用有参数的hello函数

progn 函数创建连续的多条表达式:

(progn
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello "you")
(other-window 1))

let 函数将值"you"绑定到局部变量local-name上
("绑定到局部变量"的意思等同于"创建局部变量", 只是术语不同罢了),
接下来在调用hello函数时传入此local-name变量
(let 也可以创建连续的多条表达式, 此处就不用 progn 了, 显然之后的参数全部都是此local-name变量的作用域范围):

(let ((local-name "you"))
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello local-name)
(other-window 1))
; 绑定多个变量
(let ((zebra 'stripes)
(tiger 'fierce))
(message "One kind of animal has %s and another is %s."
zebra tiger))
; 变量的默认值为nil
(let ((birch 3)
pine
fir
(oak 'some))
(message "Here are %d variables with %s, %s, and %s value."
birch pine fir oak))
; Here are 3 variables with nil, nil, and some value."

(format "Hello %s!\n" "visitor") 格式化字符串

混合了函数参数和局部变量:

(defun greeting (name)
(let ((user-name "John Wick"))
(insert (format "Hello %s!\nI am %s."
name
your-name))))

(read-from-minibuffer "Enter your name: ") 从minibuffer读取用户输入

(if (> 5 4)
(message "5 is greater than 4!"); then-part
(message "5 is not greater than 4!")); else-part
(> 5 4); 返回 t, 意为真值
'(this
list
includes
"text between quotation marks.")
; 创建一个值为列表的变量(如果不在列表前加上'则Sarah会被认为是一个函数)
(setq list-of-names '("Sarah" "Chole" "Mathilde"))
; 构造一个在原列表头部加入新元素的新列表
(cons 'buttercup ()); (buttercup)
(cons 'daisy '(buttercup)); (daisy buttercup)
; 获取列表长度
(length '(buttercup)); 1
; 获取列表的第一个值(相当于Haskell的head)
(car list-of-names)
; 获取列表第一个值以外的值(相当于Haskell的tail)
(cdr list-of-names)
; cdr的重复x次版本
(nthcdr 2 '(pine fir oak maple)); (oak maple)
; 原地设置列表项的值
(setq animals '(giraffe antelope tiger lion))
(setcar animals 'hippopotamus); (hippopotamus antelope tiger lion)
(setq domesticated-animals '(horse cow sheep goat))
(setcdr domesticated-animals '(cat dog)); (horse cat dog)
; 把一个值加到列表的开头(函数式语言里列表数据结构的push方向和命令式语言相反), push是少数直接对列表操作的函数, 不遵守函数式的不可变范式
(push "Stephanie" list-of-names)
; 为list-of-names的每一个值调用hello函数(返回mapped之后的列表)(如果不在hello函数前加上'则会被当作是一个名为hello的变量然后出错)
(mapcar 'hello list-of-names)
(while x y); 以x的值为nil时退出循环, 其他值执行循环体y
; 对列表循环
(while list
(print (car list))
(setq list (cdr list))); 重新给list赋值
; 计数器循环
(while (< count desired-number)
; body...
(setq count (1+ count))); count自增

当查找到"Hello"时将其替换为"Bonjour":

(while (search-forward "Hello")
(replace-match "Bonjour"))
(buffer-name); 文件名
(buffer-file-name); 完整路径名
(point); 返回当前光标位置
(buffer-size); 返回整个文件的缓冲区大小(字符数量)

(insert "Hello World") 在光标处插入字符串Hello World
(insert "Hello" " World") 与上一行一样, 但传了两个参数
(insert "Hello, I am " my-name) 在光标处插入字符串"Hello "和变量my-name的值

绑定F1到函数func:

(global-set-key (kbd "<f1>") 'func)

C-h i m elisp 查看官方手册

C-h f <函数名> 查询函数文档
C-h v <变量名> 查询变量文档