lisp介绍

Lisp(历史上拼写为LISP)是具有悠久历史的计算机编程语言家族,有独特和完全括号的前缀符号表示法。起源于公元1958年,是现今第二悠久而仍广泛使用的高端编程语言。只有FORTRAN编程语言比它更早一年。Lisp编程语族已经演变出许多种方言。现代最著名的通用编程语种是Clojure、Common Lisp和Scheme。

Lisp最初创建时受到阿隆佐·邱奇的lambda演算的影响,用来作为计算机程序实用的数学表达。因为是早期的高端编程语言之一,它很快成为人工智能研究中最受欢迎的编程语言。在计算机科学领域,Lisp开创了许多先驱概念,包括:树结构、自动存储器管理、动态类型、条件表达式、高端函数、递归、自主(self-hosting)编译器、读取﹣求值﹣输出循环(英语:Read-Eval-Print Loop,REPL)。

“LISP"名称源自“列表处理器”(英语:LISt Processor)的缩写。列表是Lisp的主要数据结构之一,Lisp编程代码也同样由列表组成。因此,Lisp程序可以把源代码当作数据结构进行操作,而使用其中的宏系统,开发人员可将自己定义的新语法或领域专用的语言,嵌入在Lisp编程中。

代码和数据的可互换性为Lisp提供了立即可识别的语法。所有的Lisp程序代码都写为S-表达式或以括号表示的列表。函数调用或语义形式也同样写成列表,首先是函数或操作符的名称,然后接着是一或多个参数:例如,取三个参数的函数f即为(f arg1 arg2 arg3)。

Lisp语言的主要现代版本包括Common Lisp, Scheme,Racket以及Clojure。1980年代盖伊·史提尔二世编写了Common Lisp试图进行标准化,这个标准被大多数解释器和编译器所接受。还有一种是编辑器Emacs所派生出来的Emacs Lisp(而Emacs正是用Lisp作为扩展语言进行功能扩展)非常流行,并创建了自己的标准。

Elisp

概览

运行emacs-lisp的几种方式

key command description
`C-x C-e` `eval-last-sexp` 在S表达式结尾运行,在minibuffer显示结果
`C-j` `eval-print-last-sexp` 在S表达式结尾运行,打印运行结果
`M-:` `eval-expression` 在minibuffer输入命令并执行
`M-x ielm` `ielm` 使用IELM解释器运行代码

创建命令(interactive函数)

;; example
(defun buffer/insert-filename ()
  "Insert file path of current buffer at current point"
  (interactive)
  (insert (buffer-file-name (current-buffer))))

emacs探索

key command description
`C-h k` `describe-key` 运行命令后,继续按键,查看此时按键绑定的函数
`C-h b` `describe-bindings` 在\*Help\*界面搜索 `Major Mode Bindings:` 可以查看所有与当前major mode相关的按键。
`C-h f` `describe-function` 查看函数文档及详细代码

elisp编程的基本设置

三个有用的pcakage:

  • rainbow-delimiters: 不同颜色区分不同层级的括号
  • paredit: 检查括号匹配
  • company: elisp代码补全

基本运算

算术

ELISP> (+ 20 30)
50
ELISP> (- 100 80)
20
ELISP> (+ 1 2 3 4 5 6)
21
ELISP> (* 1 2 3 4 5 6)
720
ELISP> (/ 1 100)
0

ELISP> (> 10 1) ;; ?? 10 > 1
t
ELISP> (< 2 8) ;; ?? 2 < 8
t
ELISP> (< 8 2) ;; ?? 8 < 2
nil

ELISP> (= 2 2)
t
ELISP> (= 2 4)
nil

ELISP> (/= 2 2)
nil
ELISP> (exp -1)
0.36787944117144233
ELISP> (log 10)
2.302585092994046
ELISP> (sin pi)
1.2246467991473532e-16
ELISP> (cos pi)
-1.0
ELISP> (tan (/ pi 2))
1.633123935319537e+16
ELISP>

比较

;;;; Compare Numbers
ELISP> (= 2 (+ 1 1))
t

;;; Compare Symbols and Numbers
ELISP> (eq 1 1)
t
ELISP> (eq 1 2)
nil
ELISP>

ELISP> (eq 'x 'x)
t
ELISP>

;;; Compare Elements of a List
ELISP> (equal (list 1 2 3 4) (list 1 2 3 4))
t

;;; Compare Strings
ELISP> (string= "hello" "hello")
t

列表

ELISP> '(10 20 30 40)
(10 20 30 40)

ELISP> '(10 203 40 "hello" () ("empty" 65))
(10 203 40 "hello" nil
    ("empty" 65))

类型判断和Literals

Emacs Literals

;;; Numbers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ELISP> 1e3
1000.0

;;; String
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ELISP> "Hello World Emacs Literals"
"Hello World Emacs Literals"
ELISP>

;;; Symbol
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> 'this-a-symbol
this-a-symbol

ELISP> 'vector->list
vector->list

ELISP> 'symbol?
symbol\?
ELISP>

;; Boolean t and nil
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ELISP> t
t
ELISP> nil
nil
ELISP>

 ;;; Everything that is not "nil" is true:
 ;;-----------------------------------------
ELISP> (if t "It is true (not nil)" "It is false (it is nil)")
"It is true (not nil)"
ELISP>
ELISP> (if 100e3 "It is true (not nil)" "It is false (it is nil)")
"It is true (not nil)"
ELISP> (if '(a b c d)  "It is true (not nil)" "It is false (it is nil)")
"It is true (not nil)"
ELISP>

ELISP> (if nil  "It is true (not nil)" "It is false (it is nil)")
"It is false (it is nil)"
ELISP>

;;; Pair / Cons Cell
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ELISP> '(a . b)
(a . b)

ELISP> '(a . 2999)
(a . 2999)

;;; List
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ELISP> '(1 2 3 (3 4) (5 6 (+ 3 4)) 10 'a 'b "hello" )
(1 2 3
   (3 4)
   (5 6
      (+ 3 4))
   10 'a 'b "hello")

ELISP> '(+ 1 2 3 4 5)
(+ 1 2 3 4 5)

ELISP> '(cos 10)
(cos 10)

;;; Vectors
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ELISP> [1 2 3 4 (+ 1 2 3 54)]
[1 2 3 4
   (+ 1 2 3 54)]

基本类型判断

Type Predicate Literal Description
Nil null nil '() Test if argument is nil
Numbers numberp 100, 200e3 Test if it is number.
String stringp "hello" Test if it is string
Symbol symbolp 'sym :keyworkd Test if it is a symbol.
       
Atom atom 'x "h" :key 200 Everything that is not a list or pair is an atom.
List listp '(1 2 x y) Test if it is a list
Pair consp '(a . 200) Test if it is a pair (cons cell)
Vector vectorp [1 200 'sym] Test if it is a vector
Object Predicate
Buffer bufferp
Window windowp
Frame framep
Process processp
ELISP> (null nil)
t
ELISP>
ELISP> (null '())
t

ELISP> (null 10)
nil

ELISP> (atom 10)
t
ELISP> (atom '(a . b))
nil
ELISP> (atom "hello world")
t
ELISP>

ELISP> (bufferp (current-buffer))
t
ELISP> (bufferp (selected-window))
nil
ELISP> (windowp (selected-window))
t
ELISP>

获取对象类型

ELISP> (type-of (current-buffer))
buffer
ELISP>
ELISP> (type-of (selected-window))
window
ELISP>

ELISP> (equal 'buffer (type-of (current-buffer)))
t
ELISP> (equal 'buffer (type-of (selected-window)))
nil
ELISP>

变量定义

;;; Constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> (defconst zsh-shell "/usr/bin/zsh")
zsh-shell

ELISP> zsh-shell
"/usr/bin/zsh"
ELISP>

;;; Define a variable
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;; Set is not used very much
;;
ELISP> (set 'avar "hello world")
"hello world"

ELISP> avar
"hello world"
ELISP>

;;;;; The most used command for assignment is setq
;;
ELISP> (setq x 10)
10

ELISP> (setq avar "hello world")
"hello world"

ELISP> x
10

ELISP> avar
"hello world"
ELISP>

ELISP> (setq my-list '(10 20 30 40))
(10 20 30 40)

ELISP> my-list
(10 20 30 40)

;;; Multiple Assignment
;;
ELISP> (setq a 10 b 20 c "Emacs")
"Emacs"
ELISP> a
10
ELISP> b
20
ELISP> c
"Emacs"
ELISP>

;; Dynamic Scoping  (Local Variables)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
ELISP> (let ((x 1) (y 10)) (+ (* 4 x) (* 5 y)) )
54
ELISP> x
    ** Eval error **  Symbol's value as variable is void: x
ELISP> y
    ** Eval error **  Symbol's value as variable is void: y
ELISP>

函数定义

定义简单函数

语法: (defun () ())

ELISP> (defun afunction (a b c) (+ a b c))
afunction

ELISP> (afunction 10 20 30)
60

ELISP> (defun myfun () (message "Hello Emacs"))
myfun
ELISP> (myfun)
"Hello Emacs"
ELISP>


ELISP>
ELISP> (defun signum (n)
     (cond ((> n 0) 1 )
       ((< n 0) -1)
       (0)))
signum
ELISP> (signum 10)
1
ELISP> (signum 0)
0
ELISP> (signum -23)
-1
ELISP>


ELISP> (defun factorial (n)
     (if (= n 0)
     1
     (* n (factorial (- n 1)))))
factorial

ELISP> (factorial 5)
120
ELISP

匿名函数/Lambda函数

语法: (lambda () ())

ELISP> (lambda (x) (+ x 3))
(lambda
  (x)
  (+ x 3))

;;; Applying Lambda Functions
;;

ELISP> ((lambda (x) (+ x 3)) 4)
7
ELISP> (funcall (lambda (x) (+ x 3)) 4)
7
ELISP>

;;; Storing Lambda Function in Variable
;;
;;

ELISP> (defvar add3 (lambda (x) (+ x 3)))
add3


ELISP> add3
(lambda
  (x)
  (+ x 3))

ELISP> (funcall add3 10)
13

ELISP> (add3 10)
    ** Eval error **  Symbol's function definition is void: add3

ELISP> (funcall #'add3 10)
    ** Eval error **  Symbol's function definition is void: add3
ELISP>

;;; Passing Lambda Function to functions
;;
ELISP> (mapcar (lambda (x) (+ x 3))  '(1 2 3 4 5))
(4 5 6 7 8)

函数作为参数

语法: (caller-function #' #' arg1 arg2 …)

在函数内部,使用 funcall 调用函数作为参数

ELISP> (mapcar log '(1 10 100 1000))
    ** Eval error **  Symbol's value as variable is void: log


ELISP> (mapcar #'log10 '(1 10 100 1000))
(0.0 1.0 2.0 3.0)

(defun sum-fun (f1 f2 x)
  (+ (funcall f1 x) (funcall f2 x)))

ELISP> (sum-fun #'log #'exp 3)
21.18414921185578
ELISP>

ELISP> (+ (log 3) (exp 3))
21.18414921185578
ELISP>

ELISP> (sum-fun (lambda (x) (* 3 x))
	(lambda (x) (* 4 x))
	5)
35
ELISP>

ELISP> (defun 1+ (x) (+ 1 x))
1+
ELISP> (defun 3* (x) (* 3 x))
3*

ELISP> (sum-fun #'1+  #'3* 4)
17
ELISP>

ELISP> (sum-fun #'1+  (lambda (x) (* 3 x)) 4)
17
ELISP>

多参函数

(defun sum (&rest numbers)
  (apply #'+ numbers))

ELISP> (sum 1 2 3 4 5 6)
21


ELISP> (apply #'sum '(1 2 3 5 6))
17

ELISP> (apply #'sum (list 1 2 3 5 (+ 6 5 2)))
24

ELISP> (apply #'sum '())
0

ELISP> (apply #'sum nil)
0

ELISP> (sum nil)
    ** Eval error **  Wrong type argument: number-or-marker-p, ni

;;----------------------------------

(defun sum-prod (a &rest xs)
  (* a (apply #'+ xs)))


ELISP> (sum-prod 3 1 2 3 4 5)
45

ELISP> (sum-prod 1 1 2 3 4 5)
15

可选参数函数

(defun test-optional (a &optional b)
  (list a b))

ELISP> (test-optional 10 20)
(10 20)

ELISP> (test-optional 10 )
(10 nil)

;--------------------------------;

(defun test-optional2 (a b &optional b c d e)
  (list :a a :b b :c c :d d :e e))

ELISP> (test-optional2 0 1 2 3 4 5 )
(:a 0 :b 2 :c 3 :d 4 :e 5)


ELISP> (test-optional2 0 1 2 3 4  )
(:a 0 :b 2 :c 3 :d 4 :e nil)

ELISP> (test-optional2 0 1 2 3   )
(:a 0 :b 2 :c 3 :d nil :e nil)

ELISP> (test-optional2 0 1 2    )
(:a 0 :b 2 :c nil :d nil :e nil)

ELISP> (test-optional2 0 1  )
(:a 0 :b nil :c nil :d nil :e nil)

ELISP> (test-optional2 0 1)
(:a 0 :b nil :c nil :d nil :e nil)

;--------------------------------;

(defun test-optional-default-b (a &optional b)
  (if b
      (list a b)
      (list a "b is null")))

ELISP> (test-optional-default-b 1 2)
(1 2)

ELISP> (test-optional-default-b 1)
(1 "b is null")

ELISP> (test-optional-default-b 1 nil)
(1 "b is null")

含属性列表参数函数

(defun make-shell-interface (&rest params)
  "
  Create a shell interface.

  Possible parameters:

    :name      Name of shell
    :type      ['sh, 'bash, ...]
    :path      Path to program
    :buffer    Name of buffer

  "
  (let
       ((name   (plist-get params :name ))
	(type   (plist-get params :type))
	(path   (plist-get params :path))
	(buffer (plist-get params :buffer)))
    (list
     (cons 'name buffer)
     (cons 'type type)
     (cons 'path path)
     (cons 'buffer buffer))))


ELISP> (make-shell-interface :name "pylaucher" :path "/usr/bin/python" :type 'sh :buffer "pyshell")
((name . "pyshell")
 (type . sh)
 (path . "/usr/bin/python")
 (buffer . "pyshell"))

ELISP> (make-shell-interface :name "pylaucher" :path "/usr/bin/python" :type 'sh)
((name)
 (type . sh)
 (path . "/usr/bin/python")
 (buffer))

ELISP> (make-shell-interface :name "pylaucher" :path "/usr/bin/python" :type 'bash)
((name)
 (type . bash)
 (path . "/usr/bin/python")
 (buffer))

ELISP> (make-shell-interface :name "pylaucher" :path "/usr/bin/python")
((name)
 (type)
 (path . "/usr/bin/python")
 (buffer))

ELISP> (make-shell-interface :name "pylaucher" )
((name)
 (type)
 (path)
 (buffer))

ELISP> (make-shell-interface  )
((name)
 (type)
 (path)
 (buffer))

ELISP> (make-shell-interface :buffer "pyshell"  :path "/usr/bin/python" :type 'sh :name "pylaucher")
((name . "pyshell")
 (type . sh)
 (path . "/usr/bin/python")
 (buffer . "pyshell"))

Closures

elisp方言默认不支持closure,所以下面的代码不会像Scheme或Common Lisp一样执行。

参考:

支持closure的代码:

(setq lexical-binding t)

(defun make-adder (x)
  (lambda (y) (+ x y)))

ELISP> (make-adder 3)
(closure
 ((x . 3)
  t)
 (y)
 (+ x y))

ELISP> ((make-adder 3) 4)
    ** Eval error **  Invalid function: (make-adder 3)
ELISP>

ELISP> (funcall (make-adder 3) 4)
7
ELISP>

ELISP> (mapcar (make-adder 3) '(1 2 3 4 5))
(4 5 6 7 8)


;;;; Sometimes is better to create macro rather than a higher order function


(defmacro make-sum-fun (f1 f2)
  `(lambda (x) (+ (,f1 x) (,f2 x))))

ELISP>
ELISP> (funcall (make-sum-fun sin cos) 3)
-0.8488724885405782
ELISP>
ELISP> (make-sum-fun sin cos)
(closure
 (t)
 (x)
 (+
  (sin x)
  (cos x)))

ELISP> (map (make-sum-fun sin cos) '(1 2 3 4 5))
(1.3817732906760363 0.4931505902785393 -0.8488724885405782 -1.4104461161715403 -0.6752620891999122)

~/.emacs.d/init.el 中添加如下配置以支持closure.

(setq lexical-binding t)

列表操作

参考:

  • https://www.fincher.org/tips/Languages/Emacs.shtml

    ;; Defining a List
        ;;
        ;; An emacs list can contain elements of almost any type.
        ;;
        ELISP> '( "a" 2323 "b" 21.2323 "hello" "emacs" nil () (34 134) '(+ 2 3 5))
        ("a" 2323 "b" 21.2323 "hello" "emacs" nil nil
         (34 134)
         '(+ 2 3 5))
    
        ELISP> (quote (1 3 3 4 5))
        (1 3 3 4 5)
    
        ;;;;; Empty List
        ;;
        ELISP> nil
        nil
        ELISP> '()
        nil
        ELISP>
    
        ;; Length of a list
        ELISP> (length '(1 2 3 4 5 6))
        6
        ELISP>
    
    
        ;; nth element of a list
        ;;
        ELISP> (nth 0 '(0 1 2 3 4 5))
        0
        ELISP> (nth 2 '(0 1 2 3 4 5))
        2
        ELISP> (nth 5 '(0 1 2 3 4 5))
        5
        ELISP> (nth 10 '(0 1 2 3 4 5))
        nil
        ELISP>
    
    
        ;; Membership test
        ;; member returns null if the element is not member of the list
        ;;
        ELISP> (member 2 '(0 1 2 3 4 5))
        (2 3 4 5)
    
        ELISP> (member 10 '(0 1 2 3 4 5))
        nil
        ELISP>
    
        ;; Position of list element (prior to emacs 24.4)
        ;;
        ELISP> (position 7 '(5 6 7 8))
        2
    
        ELISP> (position 17 '(5 6 7 8))
        nil
        ELISP>
    
        ;; Position of list element (emacs 24.4 or later)
        ;;
        ELISP> (cl-position 7 '(5 6 7 8))
        2
    
        ELISP> (cl-position 17 '(5 6 7 8))
        nil
        ELISP>
    
        ;; cdr
        ;;
        ;; Removes first element of the list, returns the list tail.
        ;;
        ELISP> (cdr '(1 2 3 4 5))
        (2 3 4 5)
    
        ;; car
        ;;
        ;; Returns the first list element
        ;;
        ELISP> (car '(1 2 3 4 5))
        1
        ELISP>
    
    
        ;; cons
        ;;
        ;; List constructor
        ;;
        ELISP> (cons 10 '(1 2 3 4))
        (10 1 2 3 4)
    
        ELISP> (cons 1 (cons 2 (cons 3 (cons 4 (cons 5 '())))))
        (1 2 3 4 5)
    
        ;; Last element of a list
        ;;
        ;;
        ELISP> (car (last '(1 2 3 4 5)))
        5
        ELISP>
    
    
        ;; Reverse a list
        ;;
        ELISP> (reverse '(1 2 3 4 5))
        (5 4 3 2 1)
    
    
        ;; Append lists
        ;;
        ;; Note: nil also means an empty list
        ;;
        ELISP> (append '(1 2) '( "a" "b" "c" "d"))
        (1 2 "a" "b" "c" "d")
    
        ELISP> (append '(1 2) nil '( "a" "b" "c" "d") nil)
        (1 2 "a" "b" "c" "d")
    
    
    
        ;; Filter list elements given a predicate function
        ;;
        ;;
        ELISP> (remove-if-not (lambda (x) (> x 2)) '(1 2 3 4 5 6 7 8 9 10))
        (3 4 5 6 7 8 9 10)
    
        ;; Test if list is empty
        ;;
        ELISP> (null '(1 2 3 4 5))
        nil
        ELISP> (null '())
        t
        ELISP> (null nil)
        t
        ELISP>
    
        ;; Drop the firsts n elements of a list
        ;;
        ;;
        ELISP> (nthcdr 2 '(1 2 3 4))
        (3 4)
    
        ELISP> (nthcdr 3 '(1 2 3 4))
        (4)
    
        ELISP> (nthcdr 13 '(1 2 3 4))
        nil
        ELISP>
    
        ;; Delete an element of a list
        ;;
        ;;
        ELISP> (delq 1 '(1 2 3 4))
        (2 3 4)
    
    
        ELISP> (delq 10 '(1 2 3 4))
        (1 2 3 4)
    
        ;; It doesn't work to delete sublists
        ;;
        ELISP> (delq (5) '(1 2 (5) 3 4))
            ** Eval error **  Invalid function: 5
        ELISP> (delq '(5) '(1 2 (5) 3 4))
        (1 2
           (5)
           3 4)
    
        ELISP> (delete '(5) '(1 2 (5) 3 4))
        (1 2 3 4)
    
        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
        ;; Convert Vector to List
        ;;
        ;;
        ELISP> (coerce [1 2 3] 'list)
        (1 2 3)
    
        ;; Convert List to Vector
        ;;
        ELISP> (coerce '(1 2 3) 'vector)
        [1 2 3]
    
        ELISP> (number-sequence 0 10 2)
        (0 2 4 6 8 10)
    
        ELISP> (number-sequence 9 4 -1)
        (9 8 7 6 5 4)
    
    
        ;; Modify list variables.
        ;;
        ELISP> alist
        (a b c d e)
    
        ELISP> (push 'f alist)
        (f a b c d e)
    
        ELISP> alist
        (f a b c d e)
    
        ELISP> (pop alist)
        f
    
        ELISP> alist
        (a b c d e)
    
        ELISP> (pop alist)
        a
        ELISP> alist
        (b c d e)
    
        ELISP>
    

关联列表和属性列表

概览

关联列表是一系列cons对,这里我可以称作 clist 或者 由两个元素组成的列表的集合,可以称为 alist

关联列表类型:clist

键: a, x, 2 and 4 值: b, y, 3 and (1 2 3 4 5)

ELISP> '((a . b) (x . y) (2 . 3) (4 . (1 2 3 4 5)))
((a . b)
 (x . y)
 (2 . 3)
 (4 1 2 3 4 5)

ELISP> (cons 'a 'b)
(a . b)

ELISP> (cons 'a (cons 'b (cons 'c nil)))
(a b c)

关联列表类型:alist

ELISP> '((a  b) (x  y) (2  3) (4  (1 2 3 4 5)))
((a b)
 (x y)
 (2 3)
 (4
  (1 2 3 4 5)))

ELISP> (list (list 'a 'b) (list 'x 'y) (list 2 3) (list 2 '(1 2 3 4 5)))
((a b)
 (x y)
 (2 3)
 (2
  (1 2 3 4 5)))

alist 不像 clist 有歧义。

属性列表:Plist

属性列表是连续的键值对集合,它的优势是括号少和可读性高。

'(:key1 value1 :key2 value2 :key3 1002.23 :key4 (a b c d e))

ELISP> '(:key1 value1 :key2 value2 :key3 1002.23 :key4 (a b c d e))
(:key1 value1 :key2 value2 :key3 1002.23 :key4
       (a b c d e))

;;; It is more useful in configuration files

(
:key1  value1
:key2  value2
:key3  value3
:key4  (a b c d e )
)

关联列表/Alist

ELISP> (setq dict
'((pine . cones)
 (oak . acorns)
 (maple . seeds)))
((pine . cones)
 (oak . acorns)
 (maple . seeds))

ELISP> dict
((pine . cones)
 (oak . acorns)
 (maple . seeds))

;; Get a cell associated with a key
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ELISP>
ELISP> (assoc 'oak dict)
(oak . acorns)

ELISP> (assoc 'wrong dict)
nil

;; Get a Key
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> (car (assoc 'oak dict))
oak
ELISP> (cdr (assoc 'oak dict))
acorns
ELISP>


ELISP> (car (assoc 'oak dict))
oak
ELISP>

;; Get all keys
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> (mapcar #'car dict)
(pine oak maple)

;; Get all values
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> (mapcar #'cdr dict)
(cones acorns seeds)

例:过滤多个键

ELISP> (defvar language-list
  '(
   ("io" . ((:command . "io")
         (:description . "Run IO Language script")))
    ("lua" . ((:command . "lua")
          (:description . "Run Lua script")))
    ("groovy" . ((:command . "groovy")
         (:description . "Run Groovy")))
    ("scala" . ((:command . "scala")
        (:cmdopt . "-Dfile.encoding=UTF-8")
        (:description . "Run Scala file with scala command")))

    ("haml" . ((:command . "haml")
           (:exec    . "%c %o %s")
           (:description . "Convert HAML to HTML")))
    ("sass" . ((:command . "sass")
           (:exec    . "%c %o --no-cac")))
 ))
language-list


ELISP> (assoc  "scala"  language-list )
("scala"
 (:command . "scala")
 (:cmdopt . "-Dfile.encoding=UTF-8")
 (:description . "Run Scala file with scala command"))

ELISP> (assoc  "lua"  language-list )
("lua"
 (:command . "lua")
 (:description . "Run Lua script"))

ELISP> (assoc  "wrong"  language-list )
nil

ELISP> (assoc ':command (assoc  "scala"  language-list ))
(:command . "scala")

ELISP> (cdr (assoc ':command (assoc  "scala"  language-list )))
"scala"
ELISP>

ELISP> (assoc ':description (assoc  "scala"  language-list ))
(:description . "Run Scala file with scala command")

ELISP> (cdr (assoc ':description (assoc  "scala"  language-list )))
"Run Scala file with scala command"
ELISP>

ELISP> (mapcar 'car language-list)
("io" "lua" "groovy" "scala" "haml" "sass")

ELISP> (mapcar 'cdr language-list)
(((:command . "io")
  (:description . "Run IO Language script"))
 ((:command . "lua")
  (:description . "Run Lua script"))
 ((:command . "groovy")
  (:description . "Run Groovy"))
 ((:command . "scala")
  (:cmdopt . "-Dfile.encoding=UTF-8")
  (:description . "Run Scala file with scala command"))
 ((:command . "haml")
  (:exec . "%c %o %s")
  (:description . "Convert HAML to HTML"))
 ((:command . "sass")
  (:exec . "%c %o --no-cac")))

ELISP>

ELISP> (mapcar (lambda (x) (
                 list
                 (car x)
                 (cdr x)
                 ))
                language-list)
(("io"
  ((:command . "io")
   (:description . "Run IO Language script")))
 ("lua"
  ((:command . "lua")
   (:description . "Run Lua script")))
 ("groovy"
  ((:command . "groovy")
   (:description . "Run Groovy")))
 ("scala"
  ((:command . "scala")
   (:cmdopt . "-Dfile.encoding=UTF-8")
   (:description . "Run Scala file with scala command")))
 ("haml"
  ((:command . "haml")
   (:exec . "%c %o %s")
   (:description . "Convert HAML to HTML")))
 ("sass"
  ((:command . "sass")
   (:exec . "%c %o --no-cac"))))

ELISP>

ELISP> (mapcar (lambda (x) (
     list
     (car x)
     (assoc ':command       (cdr x))
     (assoc ':cmdopt        (cdr x))
     (assoc ':description   (cdr x))
     ))
    language-list)

(("io"
  (:command . "io")
  nil
  (:description . "Run IO Language script"))
 ("lua"
  (:command . "lua")
  nil
  (:description . "Run Lua script"))
 ("groovy"
  (:command . "groovy")
  nil
  (:description . "Run Groovy"))
 ("scala"
  (:command . "scala")
  (:cmdopt . "-Dfile.encoding=UTF-8")
  (:description . "Run Scala file with scala command"))
 ("haml"
  (:command . "haml")
  nil
  (:description . "Convert HAML to HTML"))
 ("sass"
  (:command . "sass")
  nil nil))

ELISP>


ELISP> (mapcar (lambda (x) (
     list
     (car x)
     (cdr (assoc ':command   (cdr x)))
     (cdr (assoc ':cmdopt       (cdr x)))
     (cdr (assoc ':description   (cdr x)))
     ))

	language-list)
(("io" "io" nil "Run IO Language script")
 ("lua" "lua" nil "Run Lua script")
 ("groovy" "groovy" nil "Run Groovy")
 ("scala" "scala" "-Dfile.encoding=UTF-8" "Run Scala file with scala command")
 ("haml" "haml" nil "Convert HAML to HTML")
 ("sass" "sass" nil nil))

ELISP>

ELISP> (defun get-value (alist key) (cdr (assoc key alist)))
get-value
ELISP> (get-value language-list "scala")
((:command . "scala")
 (:cmdopt . "-Dfile.encoding=UTF-8")
 (:description . "Run Scala file with scala command"))

ELISP> (get-value language-list "lua")
((:command . "lua")
 (:description . "Run Lua script"))

ELISP>
ELISP> (get-value language-list "0")
nil
ELISP>


ELISP> (defun get-key-value (alist key field)
        (cdr (assoc  field  (cdr (assoc key alist))  )))
get-key-value
ELISP>
ELISP> (get-key-value language-list "scala" ':description)
"Run Scala file with scala command"
ELISP>

ELISP> (get-key-value language-list "scala" ':command)
"scala"
ELISP>

属性列表

ELISP> (defvar plst (list :buffer (current-buffer) :line 10 :pos 2000))
plst

ELISP>
ELISP> (plist-get plst :line)
10

ELISP> (plist-get plst :pos)
2000

ELISP> (plist-get plst :buffer)
#<buffer *ielm*>
ELISP>

ELISP>
ELISP> (plist-get plst :buffdfds)
nil
ELISP>

ELISP> (plist-member plst :buffer)
(:buffer #<buffer *ielm*> :line 10 :pos 2000)

ELISP> (plist-member plst :bufferasd)
nil
ELISP>

ELISP> (plist-put plst :winconf (current-window-configuration))
(:buffer #<buffer *ielm*> :line 10 :pos 2000 :winconf #<window-configuration>)

ELISP> plst
(:buffer #<buffer *ielm*> :line 10 :pos 2000 :winconf #<window-configuration>)

ELISP>

转换Alist成Plist和vice-versa

;; Alist to plist
(defun plist->alist (plist)
  (if (null plist)
      '()
      (cons
       (list (car plist) (cadr plist))
       (plist->alist (cddr plist)))))

ELISP> (plist->alist (list :x 10 :y 20 :name "point"))
((:x 10)
 (:y 20)
 (:name "point"))

;;; Converts association list to plist
(defun alist->plist (assocl)
  (if (null assocl)
      '()
    (let
    ((hd (car assocl))
     (tl (cdr assocl)))
      (cons (car hd)
	(cons (cadr hd)
      (alist->plist tl))))))

;;; Converts plist to clist (List of cons pairs)
(defun plist->clist (plist)
  (if (null plist)
      '()
      (cons
       (cons (car plist) (cadr plist))
      (plist->clist (cddr plist)))))

ELISP> (plist->clist (list :x 10 :y 20 :name "point"))
((:x . 10)
 (:y . 20)
 (:name . "point"))

;; Separates a property list into two lists of keys and values.
;;
(defun plist->kv (plist)
  (let ((alist (plist->alist plist)))
    (cons
     (mapcar #'car alist)
     (mapcar #'cdr alist))))

ELISP> (setq al (plist->alist (list :x 10 :y 20 :name "point")))
((:x 10)
 (:y 20)
 (:name "point"))

ELISP> (alist->plist al)
(:x 10 :y 20 :name "point")

ELISP>

(setq keylist
    '("M-i"  'previous-line
      "M-j"  'backward-char
      "M-k"  'next-line
      "M-l"  'forward-char))


ELISP> (setq kv (plist->kv keylist))
(("M-i" "M-j" "M-k" "M-l")
 ('previous-line)
 ('backward-char)
 ('next-line)
 ('forward-char))

ELISP> (car kv)
("M-i" "M-j" "M-k" "M-l")

ELISP> (cdr kv)
(('previous-line)
 ('backward-char)
 ('next-line)
 ('forward-char))

ELISP>

字符串

;; Split String

ELISP> (split-string "  two words ")
("two" "words")

ELISP>

ELISP> (split-string "o\no\no" "\n" t)
("o" "o" "o")

ELISP> (split-string "Soup is good food" "o*" t)
("S" "u" "p" " " "i" "s" " " "g" "d" " " "f" "d")

ELISP>

;; Format String

ELISP> (format-time-string "%Y/%m/%d %H:%M:%S" (current-time))
"2015/06/26 06:10:04"
ELISP>
ELISP>


;; Concatenate Strings

ELISP> (concat "The " "quick brown " "fox.")
"The quick brown fox."
ELISP>

ELISP> (mapconcat 'identity '("aaa" "bbb" "ccc") ",")
"aaa,bbb,ccc"
ELISP> (split-string "aaa,bbb,ccc" ",")
ELISP> (split-string "aaa,bbb,ccc" ",")
("aaa" "bbb" "ccc")

;; String Width

ELISP> (string-width "hello world")
11
ELISP>
ELISP> (substring "Freedom Land" 0 5)
"Freed"
ELISP>
ELISP> (string-match "ce" "central park")
0
ELISP> (string-match "gt" "central park")
nil
ELISP>


;;;;; Misc

ELISP> (make-string 5 ?x)
"xxxxx"
ELISP> (make-string 5 ?a)
"aaaaa"
ELISP> (make-string 5 ?r)
"rrrrr"
ELISP> (make-string 15 ?r)
"rrrrrrrrrrrrrrr"
ELISP>

elisp符号/字符串转换

; Convert a symbol to string
ELISP> (symbol-name 'wombat)
"wombat"

; Convert a String to Symbol
ELISP> (intern "wombat")
wombat

读取字符串中的S表达式

ELISP> (read-from-string
        "(
           (POINT1  (X  10.2323)  (Y   20.2323))
           (POINT2  (x  0.2)          (Y 923.23))
           (POINT3  (x -10.5)       (Y 78,23))
         )")
(((POINT1
   (X 10.2323)
   (Y 20.2323))
  (POINT2
   (x 0.2)
   (Y 923.23))
  (POINT3
   (x -10.5)
   (Y 78
      (\, 23))))
 . 174)

ELISP>

符号

;;; Convert a string to symbol

ELISP> (intern "a-symbol")
a-synmbol
ELISP> (symbolp (intern "a-symbol"))
t
ELISP>

;;; Convert a symbol to a string

ELISP> (symbol-name 'symbol)
"symbol"
ELISP>

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> (setq sym '(1 2 3 4 5))
(1 2 3 4 5)

ELISP> sym
(1 2 3 4 5)

;;; Test if variable is defined
ELISP> (boundp 'sym)
t
ELISP>

;;; Test if variable sym is a symbol
ELISP> (symbolp sym)
nil

;;; Test if the symbol sym is a symbol
ELISP> (symbolp 'sym)
t
ELISP>

;; Get symbol as string
;;
ELISP> (symbol-name 'sym)
"sym"

;; Get value from a symbol
;;
ELISP> (symbol-value 'sym)
(1 2 3 4 5)

ELISP> (symbol-function 'sym)
nil

ELISP> (symbol-plist 'sym)
nil

;;-------------------------;;

ELISP> (defun func (x y) (+ (* 3 x) (* 4 y)))
func

ELISP> (func 10 2)
38
ELISP>

;;; Check if function is defined
;;
ELISP> (fboundp 'func)
t
ELISP> (fboundp 'sym)
nil
ELISP>


ELISP> (symbol-name 'func)
"func"

ELISP> (symbol-value 'func)
    ** Eval error **  Symbol's value as variable is void: func
ELISP> (symbol-function 'func)
(lambda
  (x y)
  (+
   (* 3 x)
   (* 4 y)))

ELISP> (symbol-plist 'func)
nil
ELISP>

;;; Function Source Code

ELISP> (symbol-function #'func)
(lambda
  (x y)
  (+
   (* 3 x)
   (* 4 y)))


;; Test if function is an elisp primitive

ELISP> (subrp (symbol-function 'goto-char))
t
ELISP>

类型转换

类型查询

ELISP> (type-of 1000)
integer

ELISP> (type-of 1000.3434)
float
ELISP>

ELISP> (type-of "lisp")
string

ELISP> (type-of '(1 2 3 4 5))
cons
ELISP> (type-of (list 'cos 'sin 1 2 3 4 5))
cons
ELISP>

ELISP> (type-of [1 2 3 4])
vector

ELISP> (type-of 'elisp-mode-map)
symbol
ELISP>

ELISP> (type-of #'cos)
symbol
ELISP>

类型判断

;; Test if it is a number
;;-----------------------------------

ELISP> (numberp 1000)
t
ELISP> (numberp 10e4)
t
ELISP> (numberp '(1 2 3 4))
nil
ELISP> (numberp "hello world")
nil
ELISP>


;; Test if it is a string
;;-----------------------------------

ELISP> (stringp "Emacs")
t
ELISP> (stringp '(1 2 3 4))
nil
ELISP>

;; Test if ti is a symbol
;;------------------------------------
ELISP> (symbolp 'emacs)
t
ELISP> (symbolp #'emacs)
t
ELISP> (symbolp "something")
nil
ELISP> (symbolp 10000)
nil
ELISP>


;; Test if it is a list
;;-----------------------------------

ELISP> (listp '(1 2 3 4))
t
ELISP> (listp [1 2 3 4])
nil
ELISP> (listp "hello world")
nil
ELISP>


;; Test if it is a vector
;;-----------------------------------

ELISP> (vectorp ["Lisp" "Emacs" "Scheme" "Clojure"])
t
ELISP>
ELISP> (vectorp '(1 2 3))
nil
ELISP> (vectorp "lisp")
nil
ELISP>

数字/字符串转换

ELISP>
ELISP> (number-to-string 1000)
"1000"

ELISP> (string-to-number "200")
200
ELISP>
ELISP>

符号/字符串转换

ELISP> (symbol-name 'my-symbol)
"my-symbol"

ELISP> (symbol-name :my-symbol)
":my-symbol"
ELISP>

ELISP> (intern "some-symbol")
some-symbol

S表达式/字符串转换

  • read: 解析S表达式

    ELISP>
        ELISP> (setq raw "(:x 10 :y 20 :z 30 :w \"hello world\")")
        "(:x 10 :y 20 :z 30 :w \"hello world\")"
        ELISP>
        ELISP> (read raw)
        (:x 10 :y 20 :z 30 :w "hello world")
    
        ELISP> (plist-get (read raw) :x)
        10
        ELISP> (plist-get (read raw) :w)
        "hello world"
        ELISP>
    
    -   prin1-to-string: 序列化S表达式
    
        ELISP> (setq sexp '(:x 10 :y 20 :z 30 :w "hello world"))
        (:x 10 :y 20 :z 30 :w "hello world")
    
        ELISP> sexp
        (:x 10 :y 20 :z 30 :w "hello world")
    
        ELISP> (prin1-to-string sexp)
        "(:x 10 :y 20 :z 30 :w \"hello world\")"
        ELISP>
    

求值

S表达式求值

ELISP> (eval '(+ 1 2 3 4 5))
15
ELISP>


ELISP> '(defun func1(x)(* 10 x))
(defun func1
    (x)
  (* 10 x))

ELISP>

ELISP> '((+ 1 3) (* 4 5) (- 8 9))
((+ 1 3)
 (* 4 5)
 (- 8 9))

ELISP> (eval '(defun func1(x)(* 10 x)))
func1
ELISP> (func1 5)
50
ELISP>


ELISP> (mapcar 'eval '((+ 1 3) (* 4 5) (- 8 9)))
(4 20 -1)

字符串求值

ELISP> (defun eval-string (str) (eval (read str)))
eval-string

ELISP> (eval-string "(+ 1 2 3 4 5 6)")
21
ELISP>

ELISP> (eval-string "(defun func2(x)(* 10 x)))")
func2
ELISP> (func2 6)
60
ELISP>

S表达式格式化为字符串

ELISP> (setq sexp1 '(+ 1 (* 2 3)))
(+ 1
   (* 2 3))

ELISP> (eval sexp1)
7

ELISP> (format "%S" sexp1)
"(+ 1 (* 2 3))"
ELISP>

Elisp中的求值命令

命令 功能
M-x eval-defun 函数求值
M-x eval-region 区域内表达式求值
M-x eval-buffer buffer内表达式求值
M-x eval-expression 输入框输入求值
M-x load-file 文件加载

Defalias

内置宏 defalias 可以为emaca函数定义简短的名字。

参考:Emacs: Use Alias for Fast M-x

ELISP> (require 'cl)
cl
ELISP>

ELISP> (defalias 'map 'mapcar)
map
ELISP> (map (lambda (x) (* 3 x)) (list 1 2 3 4 5 6))
(3 6 9 12 15 18)

ELISP> (defalias 'filter 'remove-if-not) ;; remove-if-not comes from "cl" library
filter

;;; Filter all buffers bounded to a file
;;
ELISP> (filter #'buffer-file-name (buffer-list))
(#<buffer README.org> #<buffer Projects.wiki.org> #<buffer Index.wiki.org> #<buffer settings.org> #<buffer project.org>)

;;; Reject all buffers which are not bounded to a file
ELISP> (reject #'buffer-file-name (buffer-list))
(#<buffer *ielm*> #<buffer *Help*> #<buffer  *Minibuf-1*> #<buffer emacs> #<buffer *scratch*> ..)

;;; The command M-x org-html-export-to-htm will export this document (README.org) to html
;;  the command M-x org2html will do so too.
;;
(defalias #'org2html #'org-html-export-to-html)

;;
;;  It is also useful to create more convenient names for Emacs API
;; in a namsepace-like fashion that makes easier to find functions and
;; autocomplete functions, for instance:
;;
(defalias 'file/extension         'file-name-extension)
(defalias 'file/extension-sans    'file-name-sans-extension)
(defalias 'file/path-expand       'expand-file-name)
(defalias 'file/filename          'file-name-nondirectory)
(defalias 'file/path-relative     'file-relative-name)
(defalias 'file/rename            'rename-file)
(defalias 'file/delete            'delete-file)
(defalias 'file/copy              'copy-file)

;;; To find the documentation of a function group defined in this fashion
;; Enter M-x apropos  and then type file/
(apropos "file/")

ELISP> (set-buffer "README.org")
#<buffer README.org>
ELISP> (buffer-file-name)
"/home/tux/PycharmProjects/emacs/README.org"
ELISP> (file/basename (buffer-file-name))
"README"
ELISP> (file/extension (buffer-file-name))
"org"
ELISP> (file/filename (buffer-file-name))
"README.org"
ELISP>

控制结构

Conditional Statement

If Else 语句

;;
;; Any value different from nil or '() is true, otherwise false.
;;

;; True
;;
ELISP> (if t 5 6)
5

ELISP> (if 10 5 6)
5

ELISP> (if 0 5 6)
5

;; False
ELISP> (if nil 5 6)
6

ELISP> (if '() 5 6)
6


;; Inverting Predicate
;;
ELISP> (if (not t) 5 6)
6

ELISP> (if (not nil) 5 6)
5


ELISP> (if (< 5 10)  (message "less than 10") (message "greater or equal to 10") )
"less than 10"

ELISP> (if (< 30 10)  (message "less than 10") (message "greater or equal to 10") )
"greater or equal to 10"
ELISP>

;;; If else with multiple statements

ELISP> (setq x 10)
10

ELISP> (if (> x 5)
       ;; Then Statement
       (progn

     (message "Positive Number")
     (print "Greater than five")
     (split-window-vertically)
     78 ;;  Return Value
	)
     ;; Else Statement
     (progn
       (print "Less than five")
       (split-window-horizontally)
       12 ;;  Return Value
     ))

"Greater than five"

78
ELISP>

When语句

ELISP> (when t 3)
3

ELISP> (when nil 3)
nil


ELISP> (setq x 5)
5

ELISP> (when (> x 3)
     (message "Less than 3"))
"Less than 3"
ELISP>

ELISP> (setq x 1)
1

ELISP> (when (> x 3)
     (message "Less than 3"))
nil
ELISP>


;;;;; When with Multiple Statements

ELISP> (setq x 10)
10

ELISP> (when (> x 7)
     (progn
       (message "Greater than 7 OK.")
       (message "Print message 2")
       (split-window-horizontally)
      ))

 #<window 8 on *ielm*>
ELISP>

Cond - Case Switch

ELISP> (setq a 3)       ;; a = 3
3
ELISP>

ELISP> (cond
	((evenp a) a)       ;; if   (a % 2 == 0)    ==> a
	((> a 7) (/ a 2))   ;; elif (a > 7)         ==> a/2
	((< a 5) (- a 1))   ;; elif (a < 5)         ==> a-1
	(t 7)               ;; else                 ==> 7
	)
2
ELISP>

CL-Case - Case Switch

(defun test-cl-case (operation x y)
  (cl-case operation
    (:mul (* x y))
    (:add (+ x y))
    (:sub (- x y))
    (:div (/ x y))
    (otherwise nil)))

ELISP> (test-cl-case :mul 2 10)
20

ELISP> (test-cl-case :sub 10 2)
8

ELISP> (test-cl-case :add 10 2)
12
ELISP> (test-cl-case :div 10 2)
5

ELISP> (test-cl-case 'dummy 20 10)
nil

循环

Dolist

ELISP> (dolist (h '(a b c)) (print h))

a

b

c

nil

ELISP> (dolist (x '(1 2 3)) (print (* 2 x)))

2

4

6

nil
ELISP>

ELISP> (dolist (x '(1 2 3))
     (dolist (y '(a b))
        (print (list x y))))
(1 a)

(1 b)

(2 a)

(2 b)

(3 a)

(3 b)

nil
ELISP>

Dotimes

ELISP> (dotimes (i 3) (print i))

0

1

2

nil
ELISP

ELISP> (dotimes (i 3) (print (* 2 i)))

0

2

4

nil
ELISP>

Loop

最好使用 mapfilter 代替 loops , 详见 Functional Programming

ELISP> (setq a 4)
4

ELISP> (loop
	(setq a (+ a 1))
	(when (> a 7) (return a)))
8

ELISP> a
8
ELISP>

ELISP> (loop
   (setq a (- a 1))
   (when (< a 3) (return)))
nil
ELISP> a
2
ELISP>

Loop Collecting / Summing / For

ELISP> (loop for i from 1 to 10 collecting i)
(1 2 3 4 5 6 7 8 9 10)

ELISP> (loop for i from 1 to 10 collecting (* 3 i))
(3 6 9 12 15 18 21 24 27 30)

ELISP> (loop for x from 1 to 10 summing (expt x 2))
385

ELISP> (loop for x from 1 to 10 collecting (* 2 x))
(2 4 6 8 10 12 14 16 18 20)

ELISP> (loop for x from 1 to 10 summing (* 2 x))
110
ELISP>

ELISP> (apply #'+ '(2 4 6 8 10 12 14 16 18 20))
110

ELISP> (loop for i below 10 collecting i)
(0 1 2 3 4 5 6 7 8 9)

ELISP>  (loop for x in '(1 2 3)
      do (print x) )

1

2

3

nil

(loop
       for x in '(a b c)
       for y in '(1 2 3 4 5 6)
       collect (list x y))
((a 1)
 (b 2)
 (c 3))

ELISP> (loop for (a b) in '((x 1) (y 2) (z 3))
      collect (list b a))
((1 x)
 (2 y)
 (3 z))

ELISP> (loop for i upto 20
      if (oddp i)
	collect i into odds
      else
	collect i into evens
      finally (return (values evens odds)))
((0 2 4 6 8 10 12 14 16 18 20)
 (1 3 5 7 9 11 13 15 17 19))

Do Loop

(do (variable-definition*)
    (end-test-form result-form*)
  statement*)

(do
   ;; Variables Definitions
   ((i 0 (1+ i)))

   ;; Test form
    ((>= i 4))

  ;; Statement form
  (print i))

0

1

2

3
nil

;; Fibbonaci Computing Loop
;;
(do ((n 0 (1+ n))
     (cur 0 next)
     (next 1 (+ cur next)))
    ((= 10 n) cur))
55

函数式编程

Dash 是emacs经常使用的函数式编程库。

  • Map and Filter

Mapcar / Equivalent to map

ELISP> (defun my-fun (x) (* x 10))
my-fun
ELISP>

ELISP> (mapcar 'my-fun '(1 2 3 5 6))
(10 20 30 50 60)

ELISP> (mapcar 'capitalize '("hello" "world" "emacs"))
("Hello" "World" "Emacs")

;;  Anonymous Functions
;;
ELISP> (mapcar (lambda (x) (* x x))   '(1 2 3 4 5 6))
(1 4 9 16 25 36)


ELISP> (setq anon (lambda (x) (* x x)))
(lambda
  (x)
  (* x x))

ELISP> (mapcar anon '(1 2 3 4 5 6))
(1 4 9 16 25 36)

Filter

ELISP> (null nil)
t
ELISP> (null 23)
nil
ELISP>

;; Equivalent to  Haskell idiom:
;;
;; > filter predicate list
;;
ELISP> (remove-if-not 'null '(1 2 3 nil 5 6 nil nil ))
(nil nil nil)

;; Equivalent to Haskell idiom:
;;
;;   > filter (\x -> not (predicate x)) list
;;
;; a more apropriate name would be reject
;;
ELISP> (remove-if 'null '(1 2 3 nil 5 6 nil nil ))
(1 2 3 5 6)



ELISP> (defun range (step start stop)
  (if (> start stop)
      nil
      (cons start (range step (+ step start) stop))

  );; End If
);; End range

ELISP> (range 1 0 10)
(0 1 2 3 4 5 6 7 8 9 10)

ELISP> (range 2 0 20)
(0 2 4 6 8 10 12 14 16 18 20)


ELISP> (remove-if (lambda (x) (= (% x 2) 0)) (range 1 0 20))
(1 3 5 7 9 11 13 15 17 19)

ELISP> (remove-if-not (lambda (x) (= (% x 2) 0)) (range 1 0 20))
(0 2 4 6 8 10 12 14 16 18 20)


ELISP> (remove-if (lambda (x) (= (% x 3) 0)) (range 1 0 20))
(1 2 4 5 7 8 10 11 13 14 16 17 19 20)

ELISP> (remove-if-not (lambda (x) (= (% x 3) 0)) (range 1 0 20))
(0 3 6 9 12 15 18)

ELISP>
  • 匿名函数/lambda函数

    ELISP> (lambda (x)(* x 10))
        (lambda
          (x)
          (* x 10))
    
        ELISP>
    
        ELISP> (funcall (lambda (x)(* x 10)) 5)
        50
        ELISP>
    
        ELISP> (setq my-lambda (lambda (x) (+ (* x 10) 5))) ;; 10 * x + 5
        (lambda
          (x)
          (+
           (* x 10)
           5))
    
        ELISP> (funcall my-lambda 10)
        105
        ELISP> (mapcar my-lambda '(1 2 3 4 5))
        (15 25 35 45 55)
    
    
        ELISP>  (setq double (function (lambda (x) (+ x x)) ))
        (lambda
          (x)
          (+ x x))
    
        ELISP> (funcall double 22)
        44
        ELISP>
    
    
        ;;
        ;; Apply a function to a list of arguments
        ;;
        ;;;;;;;;;;;
    
        ELISP> (apply #'+ '(1 2 3 4 5))
        15
        ELISP>
    
        ELISP>
        ELISP> (defun f (x y z) (+ (* 10 x) (* -4 y) (* 5 z)))
        f
        ELISP> (f 2 3 5)
        33
    
        ELISP> (apply 'f '(2 3 5))
        33
    
    
        ELISP> (mapcar (lambda (x) (apply 'f x)) '( (2 3 5) (4 5 6) (8 9 5)))
        (33 50 69)
    
    
    
        ;; Create Higher Order Functions
        ;;
        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
  • Function Composition ????

    ELISP> ;; ID: f0c736a9-afec-3e3f-455c-40997023e130
        (defun compose (&rest funs)
          "Return function composed of FUNS."
          (lexical-let ((lex-funs funs))
            (lambda (&rest args)
              (reduce 'funcall (butlast lex-funs)
                  :from-end t
                  :initial-value (apply (car (last lex-funs)) args)))))
                  compose
    
        ELISP> (funcall (compose 'prin1-to-string 'random* 'exp) 10)
        "4757.245739507558"
        ELISP>
    
  • Interactive Functions

    (defun some-interactive-function ()
           "Documentation"
          (interactive)
          ...)
    
  • List Recursive Functions

Map

(defun map (fun xs)
  (if (null xs)
      '()
    (cons (funcall fun (car xs))
      (map fun (cdr xs)))))

ELISP> (map #'buffer-name (buffer-list))
("*ielm*" "*scratch*" " *Minibuf-1*" "*Backtrace*" "*eshell*" "sclj.import.scm" "*Messages*" "*GNU Emacs*" " *Minibuf-0*" " *code-conversion-work*" " *Echo Area 0*" " *Echo Area 1*" "*Shell Command Output*" "*Completions*")

ELISP>

Filter

(defun filter (fun xs)
  (if (null xs)
      '()
    (let ((hd (car xs))
      (tl (cdr xs)))
      (if (funcall fun hd)
      (cons hd (filter fun tl))
    (filter fun tl)))))

(defun odd? (x) (zerop (% x 2)))

ELISP> (filter #'odd? '(1 2 3 4 5 6))
(2 4 6)

Take

(defun take (n xs)
  (if (or (null xs) (zerop n))
      '()
    (cons (car xs)
          (take (- n 1) (cdr xs)))))


ELISP> (take 5 '(a b c d e f g h i j))
(a b c d e)

ELISP> (take 10 '(a b c d e f g h i j))
(a b c d e f g h i j)

ELISP> (take 200 '(a b c d e f g h i j))
(a b c d e f g h i j)

ELISP> (take 0 '(a b c d e f g h i j))
nil
ELISP> (take 10 '())
nil
ELISP>

Drop

(defun drop (n xs)
  (if (or (null xs) (zerop n))
      xs
    (drop (- n 1)  (cdr xs))))

ELISP> (drop 3 '(a b c d e f g h i j))
(d e f g h i j)

ELISP> (drop 4 '(a b c d e f g h i j))
(e f g h i j)

ELISP> (drop 25 '(a b c d e f g h i j))
nil
ELISP>

Map-apply

(defun map-apply (fun xss)
  (mapcar (lambda (xs) (apply fun xs)) xss))

ELISP> (map-apply #'fxyz '((1 2 3) (3 4 5) (2 3 1)))
(17 35 20)

ELISP> (fxyz 1 2 3)
17
ELISP> (fxyz 3 4 5)
35
ELISP> (fxyz 2 3 1)
20
ELISP>

Zip

(defun zip (&rest xss)
  (if (null (car xss))
      '()
    (cons
     (mapcar #'car xss)
     (apply #'zip (mapcar #'cdr xss)))))

ELISP> (zip (list 1 2 3 4) '(a b c d) '(x y z w))
((1 a x)
 (2 b y)
 (3 c z)
 (4 d w))

Zipwith

(defun zipwith (f &rest xss)
  (map-apply f (apply #'zip xss)))

ELISP> (zipwith #'f '(1 2 3) '(4 5 6) '(3 6 8))
(23 40 53)

ELISP> (f 1 4 3)
23

ELISP> (f 2 5 6)
40

ELISP> (f 3 6 8)
53
ELISP>

Foldr

;;           f :: x -> acc -> acc
;; foldr :: (a -> b -> b) -> b -> [a] -> b
;; foldr :: (x -> acc -> acc) -> acc -> [x] -> acc
;; foldr f z []     = z
;; foldr f z (x:xs) = f x (foldr f z xs)
;;
;;  x = (car xss) , xs = (cdr xss)
(defun foldr (f acc xss)
  (if (null xss)
      ;; foldr f z []     = z
      acc
    ;; foldr f z (x:xs) = f x (foldr f z xs)
    (funcall f (car xss)
             (foldr f acc (cdr xss)))))

ELISP> (foldr (lambda (a b) (+ (* 10 b) a)) 0 '(1 2 3 4 5))
54321
ELISP>

ELISP> (foldr #'+ 0 '(1 2 3 4 5))
15
ELISP>

Foldl

;; foldl :: (b -> a -> b) -> b -> [a] -> b
;; foldl f z []     = z
;; foldl f z (x:xs) = foldl f (f z x) xs
(defun foldl (f acc xss)
  (if (null xss)
      acc
    (foldl f (funcall f acc (car xss)) (cdr xss))))

ELISP> (foldl (lambda (a b) (+ (* 10 a) b)) 0 '(1 2 3 4 5))
12345
ELISP>

Map Pairs

(defun map-pair (func xs)
  (mapcar (lambda (x) (cons x (funcall func x))) xs))

ELISP> (map-pair #'1+ '(1 2 3 4))
((1 . 2)
 (2 . 3)
 (3 . 4)
 (4 . 5))

ELISP> (map-pair #'log10 '(1 10 100 1000 10000))
((1 . 0.0)
 (10 . 1.0)
 (100 . 2.0)
 (1000 . 3.0)
 (10000 . 4.0))

(defun buffer-mode (buffer-or-string)
  "Returns the major mode associated with a buffer."
  (with-current-buffer buffer-or-string
    major-mode))

ELISP> (map-pair #'buffer-mode (buffer-list))
((#<buffer *ielm*> . inferior-emacs-lisp-mode)
 (#<buffer *scratch*> . lisp-interaction-mode)
 (#<buffer *Backtrace*> . debugger-mode)
 (#<buffer *GNU Emacs*> . fundamental-mode)
 (#<buffer  *Minibuf-1*> . minibuffer-inactive-mode)
 (#<buffer  *Minibuf-0*> . minibuffer-inactive-mode)
 (#<buffer *Messages*> . messages-buffer-mode)

Map pairs xy

(defun map-xypair (func-x func-y xs)
  (mapcar
   (lambda (x)
     (cons (funcall func-x x) (funcall func-y x)))
   xs))

ELISP> (map-xypair #'buffer-name #'buffer-mode (buffer-list))
(("*ielm*" . inferior-emacs-lisp-mode)
 ("*scratch*" . lisp-interaction-mode)
 ("*Backtrace*" . debugger-mode)
 ("*GNU Emacs*" . fundamental-mode)
 (" *Minibuf-1*" . minibuffer-inactive-mode)
 (" *Minibuf-0*" . minibuffer-inactive-mode)
 ("*Messages*" . messages-buffer-mode)
 (" *code-conversion-work*" . fundamental-mode)
 (" *Echo Area 0*" . fundamental-mode)
 (" *Echo Area 1*" . fundamental-mode)
 (" *http www.httpbin.org:80*" . fundamental-mode)
 (" *http www.httpbin.org:80*-820734" . fundamental-mode)
 (" *http www.httpbin.org:80*-914099" . fundamental-mode)
 (" *http www.httpbin.org:80*-945998" . fundamental-mode)
 ("*Help*" . help-mode)
 ("*Completions*" . completion-list-mode))

Juxt

ELISP> (juxt #'buffer-name #'buffer-mode)
(lambda
  (x)
  (list
   ((funcall #'buffer-name x)
    (funcall #'buffer-mode x))))


ELISP> (funcall (juxt #'buffer-file-name  #'buffer-name #'buffer-mode) (current-buffer))
(nil "*ielm*" inferior-emacs-lisp-mode)

ELISP> (mapcar (juxt #'buffer-name #'buffer-file-name #'buffer-mode) (buffer-list))
(("*ielm*" nil inferior-emacs-lisp-mode)
 ("*scratch*" nil lisp-interaction-mode)
 ("passgen.py" "/home/tux/bin/passgen.py" python-mode)
 (".bashrc" "/home/tux/.bashrc" sh-mode)
 (" *Minibuf-1*" nil minibuffer-inactive-mode)
 ("init.el" "/home/tux/.emacs.d/init.el" emacs-lisp-mode)
 ("*Backtrace*" nil debugger-mode)
 ("*GNU Emacs*" nil fundamental-mode)
 (" *Minibuf-0*" nil minibuffer-inactive-mode)
 ("*Messages*" nil messages-buffer-mode)
 (" *code-conversion-work*" nil fundamental-mode)
 (" *Echo Area 0*" nil fundamental-mode)
 (" *Echo Area 1*" nil fundamental-mode)
 (" *http www.httpbin.org:80*" nil fundamental-mode)
 (" *http www.httpbin.org:80*-820734" nil fundamental-mode)
 (" *http www.httpbin.org:80*-914099" nil fundamental-mode)
 (" *http www.httpbin.org:80*-945998" nil fundamental-mode)
 ("*Help*" nil help-mode)
 ("*Completions*" nil completion-list-mode))

Map Juxt

(defmacro map-juxt (xs_f xs)
  `(mapcar (juxt ,@xs_f) ,xs))


ELISP> (map-juxt (#'buffer-name #'buffer-file-name #'buffer-mode) (buffer-list))
(("*ielm*" nil inferior-emacs-lisp-mode)
 ("*scratch*" nil lisp-interaction-mode)
 ("passgen.py" "/home/tux/bin/passgen.py" python-mode)
 (".bashrc" "/home/tux/.bashrc" sh-mode)
 (" *Minibuf-1*" nil minibuffer-inactive-mode)
 ("init.el" "/home/tux/.emacs.d/init.el" emacs-lisp-mode)
 ("*Backtrace*" nil debugger-mode)
 ("*GNU Emacs*" nil fundamental-mode)
 (" *Minibuf-0*" nil minibuffer-inactive-mode)
 ("*Messages*" nil messages-buffer-mode)
 ...

Lambda Function Macro

(defmacro $f (f &rest params)
  `(lambda ($) (,f ,@params)))


ELISP> ($f - 10 $)
(lambda
  ($)
  (- 10 $))

ELISP> ($f * (+ 3 $) 5)
(lambda
  ($)
  (*
   (+ 3 $)
   5))

ELISP> (funcall ($f * (+ 3 $) 5) 10)
65
ELISP> (mapcar  ($f * (+ 3 $) 5) '(1 2 3 4 5))
(20 25 30 35 40)

ELISP>
ELISP> (mapcar  ($f list (1+ $) (1- $) (log10 $)) '(1 10 100 1000))
((2 0 0.0)
 (11 9 1.0)
 (101 99 2.0)
 (1001 999 3.0))

Partial Application

(defmacro $c (f  &rest params)
  `(lambda (__x) (,f ,@params __x)))

ELISP> (defun f (x y z) (+ (* 3 x) (* 2 y) (* 4 z)))
f
ELISP> (f 1 2 3)
19
ELISP> ($c f 1 2)
(lambda
  (__x)
  (f 1 2 __x))

ELISP> (mapcar ($c f 1 2) '(1 2 3 4 5))
(11 15 19 23 27)

ELISP> (mapcar ($c + 1 2) '(1 2 3 4 5))
(4 5 6 7 8)

ELISP>

Structures

ELISP> (defstruct account id name balance)
account
ELISP> (make-account :id 3434 :name "John" :balance 1000.34)
[cl-struct-account 3434 "John" 1000.34]

ELISP> (setq user1 (make-account :id 3434 :name "John" :balance 1000.34))
[cl-struct-account 3434 "John" 1000.34]

ELISP> (account-name user1)
"John"

ELISP> (account-id user1)
3434

ELISP> (account-balance user1)
1000.34

;; Test if input is an account object
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ELISP> (account-p user1)
t
ELISP>

;; Change Field
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> (defun withdraw (accc amount)
         (setf (account-balance acc) (- (account-balance acc) amount)))
withdraw

ELISP> (withdraw user1 300)
700.34
ELISP> user1
[cl-struct-account 3434 "John" 700.34]

ELISP> (withdraw user1 500)
200.34000000000003
ELISP> user1
[cl-struct-account 3434 "John" 200.34000000000003]

ELISP>

;; Build structure from a list of parameters
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> (defun build-account (id name balance)
         (make-account :id id :name name  :balance balance))
build-account

ELISP> (build-account 3434 "O' Neil" 35434.23)
[cl-struct-account 3434 "O' Neil" 35434.23]

ELISP> (apply 'build-account '(3434 "O' Neil" 35434.23))
[cl-struct-account 3434 "O' Neil" 35434.23]

ELISP>

ELISP> (mapcar (lambda (params) (apply 'build-account params))
               '(
                 (34423 "O' Neil" 23.2323)
                 (1023  "John Edwards" 1002323.23)
                 (92323 "Mr. Dummy"  2323241.2323)
                 (8723  "John Oliver" 9823)
                 ))
([cl-struct-account 34423 "O' Neil" 23.2323]
 [cl-struct-account 1023 "John Edwards" 1002323.23]
 [cl-struct-account 92323 "Mr. Dummy" 2323241.2323]
 [cl-struct-account 8723 "John Oliver" 9823])

ELISP>

ELISP> (defun build-accounts-from-list (list-of-params)
         (mapcar (lambda (params) (apply 'build-account params)) list-of-params))
build-accounts-from-list
ELISP>

ELISP> (setq accounts (build-accounts-from-list
                       '(
                         (34423 "O' Neil" 23.2323)
                         (1023  "John Edwards" 1002323.23)
                         (92323 "Mr. Dummy"  2323241.2323)
                         (8723  "John Oliver" 9823)
                         )))
([cl-struct-account 34423 "O' Neil" 23.2323]
 [cl-struct-account 1023 "John Edwards" 1002323.23]
 [cl-struct-account 92323 "Mr. Dummy" 2323241.2323]
 [cl-struct-account 8723 "John Oliver" 9823])

ELISP> accounts
([cl-struct-account 34423 "O' Neil" 23.2323]
 [cl-struct-account 1023 "John Edwards" 1002323.23]
 [cl-struct-account 92323 "Mr. Dummy" 2323241.2323]
 [cl-struct-account 8723 "John Oliver" 9823])

ELISP> (mapcar #'account-id accounts)
(34423 1023 92323 8723)

ELISP>

ELISP>
ELISP> (mapcar #'account-name accounts)
("O' Neil" "John Edwards" "Mr. Dummy" "John Oliver")

ELISP>


ELISP> (mapcar #'account-balance accounts)
(23.2323 1002323.23 2323241.2323 9823)

ELISP>

宏和元编程

Quasi-quote

;;;; Quasiquote

> `(the product of 3 and 4 is ,(* 3 4))
(the product of 3 and 4 is 12)

> `("the product of 3 and 4 is" ,(* 3 4))
("the product of 3 and 4 is" 12)

> `("the value of (exp 3) is " ,(exp 3) "the value of (sqrt 100) is" ,(sqrt 100))
("the value of (exp 3) is " 20.085536923187668 "the value of (sqrt 100) is" 10.0)

> `(a ,a b ,b c ,c d ,d)
(a 10 b 20 c my-symbol d "a string")

> `((a . ,a) (b . ,b) (c . ,c) (d . ,d))
((a . 10)
 (b . 20)
 (c . my-symbol)
 (d . "a string"))

> (setq xs '(sym1 sym2 sym3))
(sym1 sym2 sym3)

> xs
(sym1 sym2 sym3)

> `(xs ,xs)
(xs
 (sym1 sym2 sym3))

> `(xs ,@xs)
(xs sym1 sym2 sym3)

> `(if (< ,a ,b) ,(+ a 4) ,d)
(if
    (< 10 20)
    14 "a string")

> (eval `(if (< ,a ,b) ,(+ a 4) ,d))
14
>

> (eval `(if (> ,a ,b) ,(+ a 4) ,d))
"a string"

;;------------------

> (setq xlist '(1 2 3 4))
(1 2 3 4)

> (setq ylist '(a b c d e))
(a b c d e)

> `(xs ,xlist ys ,ylist)
(xs
 (1 2 3 4)
 ys
 (a b c d e))

> `(xs ,@xlist ys ,@ylist)
(xs 1 2 3 4 ys a b c d e)

定义lambda函数语法糖:λ

(defmacro λ (args body)
  `(lambda ,args ,body))

ELISP> (λ (x) (+ x 3))
(lambda
  (x)
  (+ x 3))
ELISP> (mapcar (λ (x) (+ x 3)) '(1 2 3 4 5 6))
(4 5 6 7 8 9)

Set variable to nil

(defmacro nil! (var)
  `(setq ,var nil))

ELISP> (setq x 10)
10
ELISP> x
10
ELISP>

ELISP> (nil! x)
nil
ELISP> x
nil
ELISP>

ELISP> (nil! z)
nil
ELISP> z
nil
ELISP>

Create Clojure def, defn and fn special forms

(defmacro fn (args body)
  `(lambda ,args ,body))

(defmacro def (name value)
  `(setq ,name ,value))

(defmacro defn (name args body)
  `(defun ,name ,args ,body))

ELISP> (fn (x) (* x x))
(lambda
  (x)
  (* x x))

ELISP> (mapcar (fn (x) (* x x)) '(1 2 3 4 5))
(1 4 9 16 25)

ELISP> (def x 1000)
1000
ELISP> x
1000
ELISP>

ELISP> (defn f (x y z) (+ (* 3 x) (* -4 y) (* 5 z)))
f
ELISP> (f 4 5 6)
22
ELISP>

……

Emacs API

Emacs术语

Emacs Terminology Description
Point Cursor position, number of characters from beggining of the buffer to current cursor position.
Buffer Place where the user edit something. Not all buffers are bound to a file.
Mark Beginning of the selected area.
Region Selected area/ text
Frame The current window of emacs
Windows Each frame can be split in sections that Emacs documentation calls windows
Fill Word Wrap
Yank Copy
Kill Region Cut
Kill Ring Clipboard
Kill Buffer Close Buffer
Mode Line Status Bar
Font Locking Syntax Coloring

Ben’s Journal: 11 Concepts The Emacs Newbie Should Master

Emacs API

API对象

  • Buffer
  • Temporary Buffer
  • Modes
  • Mode Hooks
  • Mode Map
  • Window
  • Frame
  • Point
  • Process
  • Network Process
  • Minibuffers

Buffers

Buffer Attributes

(buffer-list)
(current-buffer)
(mapcar #'buffer-name (buffer-list))
(mapcar #'buffer-file-name (buffer-list))
(kill-buffer "init.el")
(get-buffer "*scratch*")

列出打开文件

(defun opened-files ()
  "list all opened file in current session"
  (interactive)
  (remove-if 'null (mapcar 'buffer-file-name (buffer-list))))

(opened-files)

创建新buffer

;;
;;
;; This function returns a buffer named  buffer-or-name.
;; The buffer returned does not become the current
;; buffer—this function does not change which buffer is current.
;;

ELISP> (get-buffer-create "foobar")
#<buffer foobar>
ELISP>

;;
;;  Divide the screen in two windows, and switch to the new buffer
;;  window
;;
ELISP> (switch-to-buffer-other-window "foobar")
#<buffer foobar>
ELISP>

;; Clean Current Buffer
;;
ELISP> (erase-buffer)
nil
ELISP>

;;  Edit another buffer and go back to the old buffer
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> (defun within-buffer (name function)
         (let (curbuff (current-buffer))
           (switch-to-buffer name)
           (funcall function)
           (switch-to-buffer current-buffer)
           ))

ELISP> (within-buffer "foobar" (lambda () (insert "dummy")))
#<buffer *ielm*>
ELISP>
ELISP> (lambda (x)(* x 10))
(lambda
  (x)
  (* x 10))

;;;; Translated from: http://d.hatena.ne.jp/rubikitch/20100201/elispsyntax
;;
ELISP> ;; test-buffer Create a buffer named, to write a variety of content
(with-current-buffer (get-buffer-create "test-buffer")
  ;; Empty the contents of the buffer
  (erase-buffer)
  ;; /tmp/foo.txt Make the contents inserted
  (insert-file-contents "/etc/fstab")
  ;; Insert a string
  (insert "End\n")
  ;; Write the contents of a buffer to a file
  (write-region (point-min) (point-max) "/tmp/bar.txt"))
nil
ELISP>

Buffer Mode

Show Buffers Mode

ELISP> (defun buffer-mode (buffer-or-string)
         "Returns the major mode associated with a buffer."
         (with-current-buffer buffer-or-string
           major-mode))
buffer-mode

ELISP> (mapcar (lambda (b)(
                           let
                           (
                            (name (buffer-name b))
                            (type   (buffer-mode (buffer-name b)))
                            )
                           (list name type)
                           ))
               (buffer-list))
(("*ielm*" inferior-emacs-lisp-mode)
 ("*SPEEDBAR*" speedbar-mode)
 (" *Minibuf-1*" minibuffer-inactive-mode)
 ("*scratch*" emacs-lisp-mode)
 ("test3.ml" tuareg-mode)
 ("*Help*" help-mode)
 ("*Messages*" messages-buffer-mode)
 ("sbet.ml" tuareg-mode)
 (" *Minibuf-0*" minibuffer-inactive-mode)
 ("test.el" emacs-lisp-mode)
 ...

Get Buffer Contents / Selection / Line

Get Buffer Content as String

ELISP> (defun buffer-content (name)
         (with-current-buffer name
           (buffer-substring-no-properties (point-min) (point-max))))
buffer-content
ELISP>

ELISP> (buffer-content "test3.ml")
"\n\nlet rec prodlist = function \n    | [] ... "

Get Selected text in current buffer as string

(defun get-selection ()
  "Get the text selected in current buffer as string"
  (interactive)
  (buffer-substring-no-properties (region-beginning) (region-end))
  )

Get current line in current buffer

(defun get-current-line ()
  (interactive)
  "Get current line, where the cursor lies in the current buffer"
  (replace-regexp-in-string "[\n|\s\t]+$" "" (thing-at-point 'line t))
  )

Search and Replace in the entire Buffer

(defun replace-regexp-entire-buffer (pattern replacement)
  "Perform regular-expression replacement throughout buffer."
  (interactive
   (let ((args (query-replace-read-args "Replace" t)))
     (setcdr (cdr args) nil)    ; remove third value returned from query---args
     args))
  (save-excursion
    (goto-char (point-min))
    (while (re-search-forward pattern nil t)
      (replace-match replacement))))

Point, Region, Line and Buffer

Point

Point

Function Description
(point) Current cursor position
(point-min) Minimum cursor position in current buffer. (always returns 1)
(point-max) Maximum cursor position in current buffer.
   
(line-beginning-position) Point of the beginning of current line.
(line-end-position) Point of the end of current line.
   
(region-beginning) Position of the beginning current region (selected text).
(region-end) Position of the end current region.
   
(bounds-of-thing-at-point ) Returns the cons pair '(beginning . end) position of thing at point.

Point Interface Functions

Function Description
(goto-char ) Move the cursor to a given point.
(insert ) Insert text at current point.
(buffer-substring [pmin] [pmax]) Returns the text with properties between the points and .
(buffer-substring-no-properties [pmin] pmax]) Returns the text without properties between the points.
(delete-region [pmin] [pmax]) Deletes the text between and .
> (point)
99696

> (point-min)
1


> (point-max)
185623

>  (line-beginning-position)
99774

>  (line-end-position)
99804

> (buffer-substring-no-properties
   (line-beginning-position)
   (line-end-position))

(defun delete-line ()
  (interactive)
  (delete-region  (line-beginning-position)  (line-end-position)))

(defun delete-region ()
  (interactive)
  (delete-region  (region-beginning) (region-end)))

(defun insert-end-of-buffer ()
  (interactive)

  ;; Save Current Cursor Position
  ;; and go back to initial positon when
  ;; finish this block
  (save-excursion
    (goto-char (point-max)) ;;; Go to end of buffer
    (insert "Testing insert end of buffer")
    ))

Thing at Point API

???

Message / Output

(message "Hello world")
(message-box "Time for a break.\nDrink some coffee")

Files, Directories and Path

Basic Functions

;; Get and Set current directory

ELISP> (pwd)
"Directory /home/tux/tmp/"

ELISP> (cd "/etc/")
"/etc/"

ELISP> (pwd)
"Directory /etc/"
ELISP>


ELISP> (file-name-directory "/etc/hosts")
"/etc/"

;; Expand File Name
;;
ELISP> (expand-file-name "~/")
"/home/tux/"
ELISP> (expand-file-name ".")
"/home/tux/tmp"
ELISP> (expand-file-name "..")
"/home/tux"
ELISP>


;;;;; Create a Directory
;;;
ELISP> (mkdir "dummy")
nil
ELISP> (mkdir "dummy")
** Eval error **  File exists: /home/tux/dummy
ELISP>

;;; List Directory
;;;;
;;;
ELISP> (directory-files "/home/tux/PycharmProjects/Haskell/")
("." ".." ".git" ".gitignore" ".idea" "LICENSE" "Make" "Makefile"
 "README.back.md" "README.html" "README.md" "Test.html" "build.sh" "clean.sh"
 "codes" "dict.sh" "haskell" "ocaml" "papers" "tags" "tmp")

File Name Components

ELISP> (file-name-directory "/usr/bin/env")
"/usr/bin/"
ELISP>

ELISP> (file-name-nondirectory "/usr/bin/env")
"env"
ELISP>


ELISP> (file-name-base "/home/foo/zoo1.c")
"zoo1"
ELISP> (file-name-base "/home/foo/zoo1.c.back")
"zoo1.c"

Read / Write file to a string

Read File

ELISP> (defun file-contents (filename)
  (interactive "fFind file: ")
  (with-temp-buffer
    (insert-file-contents filename) ;; 先将文件内容插入临时buffer,再读取内容
    (buffer-substring-no-properties (point-min) (point-max))))

ELISP> (file-contents "/proc/filesystems")
"nodev  sysfs\nnodev    rootfs\nnodev   ramfs\nnodev
bdev\nnodev proc\nnodev cgroup\nnode ...

Write to File

ELISP> (append-to-file "hello world" nil "/tmp/hello.txt")
nil

ELISP> (file-contents "/tmp/hello.txt")
"hello world"
ELISP>

Window Functions

Basic Window Functions

(split-window-horizontally)
(split-window-vertically)
(delete-other-windows)
(switch-to-buffer-other-window "init.el")
(delete-window)
(make-frame)
(frame-list)
(delete-frame)

Manipulate Buffer in Another Window

http://caiorss.github.io/Emacs-Elisp-Programming/Elisp_Programming.html#sec-3-9-2

Window Configuration

(current-window-configuration)
(setq w (current-window-configuration))
w
(set-window-configuration w)

;; Screen Resolution

ELISP> (x-display-pixel-width)
1366

ELISP> (x-display-pixel-height)
768
ELISP>
ELISP>

;; Resize and Set Emacs Windows position
;;
;; From: http://uce.uniovi.es/tips/Emacs/mydotemacs.html#sec-41
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ELISP> (defun resize-frame ()
  "Set size"
  (interactive)
  (set-frame-width (selected-frame) 100)
  (set-frame-height (selected-frame) 28)
  (set-frame-position (selected-frame) 0 1))
resize-frame
ELISP>

ELISP> (resize-frame)
t
ELISP>

OS Interface

Find the current operating system

Value Description
gnu GNU Hurd system.
gnu/linux GNU/Linux system.
gnu/kfreebsd GNU system with a FreeBSD kernel.
darwin Darwin (GNU-Darwin, Mac OS X)
ms-dos MS-DOS application.
windows-nt native W32 application.
cygwin compiled using the Cygwin library
system-type
system-configuration

Date and Time

(current-time)
(insert (format-time-string "%Y-%m-%d")) ;; 2019-12-15
(insert (format-time-string "%H:%M:%S")) ;; 16:11:04
(format-time-string "%d/%m/%Y %H:%M:%S" (current-time))

Call External Commands or Apps

(call-process "mpd")
(shell-command-to-string "pwd")

Environment Variables

(getenv "PATH")
(split-string (getenv "PATH") ":")
(dolist (e (split-string  (getenv "PATH") ":")) (princ (format "%s\n" e)))
exec-path
(getenv "HOME")
(setenv "JAVA_HOME" "/usr/local/java")

system-type
(eq system-type 'gnu/linux)()
(dolist (e process-environment) (princ (format "%s\n" e)))

Process Management

(process-list)
(get-process "merlin")
(mapcar 'process-name (process-list))

;;;; Buffer Process
(process-command (get-process "vterm"))
(process-id (get-process "vterm"))
(process-buffer (get-process "vterm"))
(buffer-name (process-buffer (get-process "vterm")))
(mapcar (lambda (p) (buffer-name (process-buffer p))) (process-list))
(display-buffer (process-buffer (get-process "vterm")))

;;;; Start Asyncronous Process
;;  Start the process named py, with the buffer named pybff
;;  using the command python, /usr/bin/python (on linux)
(start-process "py" "pybff" "python")
;; End the process named py
(process-send-eof "py")

(process-send-string "py" "print 'Hello world'\n")

;;;; Get Multiple Fields

(mapcar
 (lambda (p)(list
             p
             (process-name p)
             (process-command p)
             (list (process-buffer p) (buffer-name (process-buffer p)))
             (process-id p)
             (process-status p)
             ))
 (process-list))

Interfaces

Creating Quick Access Menu

(require 'easymenu)

(easy-menu-define djcb-menu global-map "Utils"
  '("Utils"
    ("Shells" ;; submenu
     ["Ielm   - Emacs Lisp Shell"       (ielm)]
     ["Eshell - Emacs Buitin Shell"    (eshell)]
     ["Native Shell "                  (shell)]
     ["---------------------" nil]
     ["Edit .bashrc" (find-file  "~/.bashrc")]
     ["Edit .profile" (find-file "~/.profile")]
     ["Edit .Xresources" (find-file "~/.Xresources")]
     ["Edit .xsessin"    (find-file "~/.xsession")]
     ["See all GNU MAN pages" ( info)]
     ["See a specific Man Page" (woman)]

     );; End of shells menu

    ("Emacs /Elisp"  ;; submenu

     ["Ielm   - Emacs Lisp Shell"  (ielm)]
     ["Eval buffer"   (eval-buffer) ]
     ["---------------------" nil]

     ["Edit  init.el" (find-file  user-init-file)]
     ["Reload init.el" (load-file user-init-file)]
     ["Open .emac.d dir" (find-file "~/.emacs.d")]
     ["List packages"     (list-packages)]
     ["Install package"   (package-install)]

     ) ;; End of Emacs / Elisp submenu
    )) ;; End of Custom Menu

Add Icons to toolbar

http://caiorss.github.io/Emacs-Elisp-Programming/Elisp_Programming.html#sec-3-11-2

Timer

run-with-timer

;;; (run-with-timer SECS REPEAT FUNCTION &rest ARGS)
(run-with-timer 5 nil
                (lambda () (message-box "happy hacking emacs!")))

(defun cofee-wait ()
  (interactive)
  (let ((minutes 3))
    (run-with-timer (* 60 minutes)  nil
                    (lambda () (message-box "Coffee done"))
                    )
    (message "Waiting for the cofee")
    ))

Emacs Modes

Mode Association with Files

;; 列出所有和拓展名相关的mode
auto-mode-alist
;; 列出与一个mode相关的所有拓展名
(remove-if-not
 (lambda (al) (equal (cdr al) 'web-mode)) auto-mode-alist)
;; 为一个mode关联拓展名
(add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))

Lisp Routines to introspect modes??

(defun show-doc (function)
  (princ (documentation function)))

(defun mode/show ()
  "  Returns all modes associated with files

     To query the file extesions associated with a mode
     use:
         > (mode/ftypes 'markdown-mode)

     for example.
  "
  (dolist (m (remove-if #'listp
                        (mapcar #'cdr auto-mode-alist))) (print m)))

(defun mode/ftypes (mode)
  "
  Get all file extension associated with a mode.

  Usage:

  ELISP> (get-mode-ftypes 'markdown-mode)
  ((\"\\.md\\'\" . markdown-mode)
  (\"\\.text\\'\" . markdown-mode)
  (\"\\.markdown\\'\" . markdown-mode)

  "
  (remove-if-not
   (lambda (al)
     (equal (cdr al) mode))
   auto-mode-alist))

ELISP> (mode/ftypes 'clojure-mode)
(("\\(?:build\\|profile\\)\\.boot\\'" . clojure-mode)
 ("\\.\\(clj\\|dtm\\|edn\\)\\'" . clojure-mode))

ELISP> (mode/ftypes 'scheme-mode)
(("\\.\\(scm\\|stk\\|ss\\|sch\\)\\'" . scheme-mode)
 ("\\.scm\\.[0-9]*\\'" . scheme-mode)
 ("\\.oak\\'" . scheme-mode))

ELISP> (show-doc #'mode/ftypes)

Get all file extension associated with a mode.

Usage:

ELISP> (get-mode-ftypes 'markdown-mode)
(("\.md\'" . markdown-mode)
 ("\.text\'" . markdown-mode)
 ("\.markdown\'" . markdown-mode))

Mode Specific Key Bindings

(define-key emacs-lisp-mode-map (kbd "<f5>")
  (lambda () (interactive) (message "Hello world")))
(add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode)

Special Variables

emacs-major-version
load-path
window-system
system-type
system-configuration
shell-file-name
user-full-name
user-mail-address
user-init-file
user-emacs-directory
exec-directory

Network

http://caiorss.github.io/Emacs-Elisp-Programming/Elisp_Programming.html#sec-3-15

正则表达式

Emacs Regex

Special characters

. any character (but newline)
\* previous character or group, repeated 0 or more time
+ previous character or group, repeated 1 or more time
? previous character or group, repeated 0 or 1 time
^ start of line
$ end of line
[…] any character between brackets
[^..] any character not in the brackets
[a-z] any character between a and z
\\ prevents interpretation of following special char
\\ or
\w word constituent
\b word boundary
\sc character with c syntax (e.g. \s- for whitespace char)
  start\end of group
\\< \\> start\end of word
\\\` \\' start\end of buffer
\\1 string matched by the first group
\n string matched by the nth group
\\{3\\} previous character or group, repeated 3 times
\\{3,\\} previous character or group, repeated 3 or more times
\\{3,6\\} previous character or group, repeated 3 to 6 times

POSIX Character classes

[:digit:] digit, same as [0-9]
[:upper:] letter in uppercase
[:space:] whitespace character, as defined by the syntax table
[:xdigit:] hexadecimal digit
[:cntrl:] control character
[:ascii:] ascii character

Syntax Classes

\s- whitespace character \s/ character quote character
\sw word constituent \s$ paired delimiter
\s\_ symbol constituent \s' expression prefix
\s. punctuation character \s< comment starter
\s( open delimiter character \s> comment starter
\s) close delimiter character \s! generic comment delimiter
\s" string quote character \s generic string delimiter
\s\\ escape character    

Emacs X Perl Regex

Emacs Regex Perl Regex Description
  ( ) Capture group
\\{ \\} { }  
\s- \s White space
\\1, \\2, \\3, \\4 $1, $2, $3 Result of capture: search, replace.
[ ] [ ] Character class
[0-9] or [:digit:] \d Digit from 0 to 9
\b \b Word boundary
\w \w Word character

Regex Commands

C-M-s incremental forward search matching regexp
C-M-r incremental backward search matching regexp

Buffer Commands

M-x replace-regexp replace string matching regexp
M-x query-replace-regexp same, but query before each replacement
M-x align-regexp align, using strings matching regexp as delimiters
M-x highlight-regexp highlight strings matching regexp
M-x grep call unix grep command and put result in a buffer
M-x lgrep user-friendly interface to the grep command
M-x rgrep recursive grep
M-x dired-do-copy-regexp copy files with names matching regexp
M-x dired-do-rename-regexp rename files matching regexp
M-x find-grep-dired display files containing matches for regexp with Dired

Line Commands

Command (M-x command) Alias Description
keep-lines delete-non-matching-lines Delete all lines except those containing matches
flush-lines delete-matching-lines Delete lines containing matches
highlight-lines-matching-regexp hi-lock-line-face-buffer Highlight lines matching regexp
occur list-matching-lines Show lines containing a match
multi-occur   Show lines in all buffers containing a match
how-many count-matches Count the number of strings matching regexp

Regex Functions

match-string

match-end

match-beginning

re-search-forward

replace-string-in-regexp

replace-string

Build regex interactively

M-x re-builder

M-x query-replace-regexp

Emacs Regex rx-notation

(require 'rx)

;;  (rx <patterns>)

ELISP> (rx digit)
"[[:digit:]]"

ELISP> (rx-to-string '(or "foo" "bar"))
"\\(?:\\(?:bar\\|foo\\)\\)"
Description rx notation Emacs regex
Beginning of Line bol ^
End of Line eol $
     
Begining of String bos \\\\\`
End of String eos `\\'`
     
Beginning of Word bow \\\\<
End of Word eow \\\\>
     
Digit 0 to 9 digit \lbr\lbr:digit:\rbr\rbr
Hexadecimal digit hex \lbr\lbr:xdigit:\rbr\rbr
Match ascii character    
Match anything lower case lower \lbr\lbr:lower:\rbr\rbr
Match anything upper case upper \lbr\lbr:upper:\rbr\rbr
     
word   \sw

example

ELISP> (require 'rx)
rx

ELISP> (rx (+ digit))
"[[:digit:]]+"

ELISP> (rx digit (+ digit))
"[[:digit:]][[:digit:]]+"

ELISP> (rx bol (+ digit) eol)
"^[[:digit:]]+$"

ELISP> (rx (zero-or-more digit))
"[[:digit:]]*"

ELISP> (rx (one-or-more digit))
"[[:digit:]]+"

ELISP> (rx (or "cat" "rat" "dog"))
"\\(?:cat\\|dog\\|rat\\)"

;; (replace-regexp-in-string REGEXP REP STRING
;;      &optional FIXEDCASE LITERAL SUBEXP START)

ELISP> (replace-regexp-in-string
          (rx (or "cat" "rat" "dog"))
          ""
          "cat cata rat rat dograt dog cat2334 23rat2")
" a     2334 232"

;; Replaces only in the beggining of line
;;
ELISP>  (replace-regexp-in-string
          (rx bol (or "cat" "rat" "dog"))
          ""
          "cat cata rat rat dograt dog cat2334 23rat2")
" cata rat rat dograt dog cat2334 23rat2"


ELISP>  (replace-regexp-in-string
          (rx bow (or "cat" "rat" "dog") eow)
          ""
          "cat cata rat rat dograt dog cat2334 23rat2")
" cata   dograt  cat2334 23rat2"

ELISP>  (rx bow (or "cat" "rat" "dog") eow)
"\\<\\(?:cat\\|dog\\|rat\\)\\>"

;;  Removes all whitespaces
;;
ELISP>  (replace-regexp-in-string
          (rx (* whitespace))
          ""
          "cat cata rat rat dograt dog cat2334 23rat2")

"catcataratratdogratdogcat233423rat"

ELISP>  (replace-regexp-in-string
          (rx (* whitespace))
          ""
          "cat cata rat rat dograt dog cat2334 23rat2")

"catcataratratdogratdogcat233423rat"

;; Capture group
;;
ELISP>  (replace-regexp-in-string
          (rx (submatch bow (or "cat" "rat" "dog") eow))
          "(\\1)"
          "cat cata rat rat dograt dog cat2334 23rat2")

"(cat) cata (rat) (rat) dograt (dog) cat2334 23rat2"

ELISP> (rx (submatch bow (or "cat" "rat" "dog") eow))
"\\(\\<\\(?:cat\\|dog\\|rat\\)\\>\\)"

Color Scheme

http://caiorss.github.io/Emacs-Elisp-Programming/Elisp_Programming.html#sec-5

Key Bindings

http://caiorss.github.io/Emacs-Elisp-Programming/Elisp_Programming.html#sec-6