A Simple List

• pcase

``````(pcase (list 1 2 3)
(`(,a ,b ,c) (+ a b c)))
``````

``````(match (list 1 2 3)
((list a b c) (+ a b c)))
``````

A PList

• pcase

``````(pcase '(:foo 1 :bar 2)
(`(:foo ,foo :bar ,bar) (cons foo bar))) ;; => (1 . 2)
``````

``````(match '(:foo 1 :bar 2)
((plist :foo foo :bar bar) (cons foo bar))) ;; => (1 . 2)
``````

An AList

• pcase

``````(pcase '((foo . 1) (bar . 2))
(`((foo . ,foo) (bar . ,bar)) (list :foo foo :bar bar))) ;; => (:foo 1 :bar 2)
``````

``````(match '((foo . 1) (bar . 2))
((alist 'foo foo 'bar bar) (list :foo foo :bar bar)))  ;; => (:foo 1 :bar 2)
``````

Rest of list

• dash

``````(pcase (list 1 2 3 4)
(`(,_ . ,rest) rest)) ;; => (2 3 4)
``````

``````(match (list 1 2 3 4)
((list-rest _ rest) rest)) ;; => (2 3 4)
``````

List of Different Lengths

• pcase

``````(pcase (list 1 2)
(`() 0)
(`(,a) a)
(`(,a ,b) (+ a b)))
``````

``````(match (list 1 2)
((list) 0)
((list a) a)
((list a b) (+ a b)))
``````

Skipping Elements

• pcase

``````(pcase (list 1 2 3 4)
(`(,a ,_ ,_ ,d) (+ a d)))
``````

``````(match (list 1 2 3 4)
((list a _ _ d) (+ a d)))
``````

Value in Patterns

• pcase

``````(pcase (list 1 2)
(`(0 ,b) b)
(`(,a ,b) (+ a b)))
``````

``````(match (list 0 0)
((list 0 b) b)
((list a b) (+ a b)))
``````

• pcase

``````(pcase '(file-error "make client process failed")
(`(file-error ,message) message))
``````

``````(match '(file-error "make client process failed")
((list 'file-error message) message))
``````

Repeated Values

• pcase

``````(pcase (list 0 1)
(`(,a ,a) "Elements are equal according to eq")
(`(,a ,b) "Elements are different"))
``````

``````(match (list 0 1)
((list a (equal a)) "Elements are equal according to eq")
((list a b) "Elements are different"))
``````

Cons Cells

• pcase

``````(pcase (cons 1 2)
(`(,a . ,b) (+ a b)))
``````

``````(match (cons 1 2)
((cons a b) (+ a b)))
``````

Where Clauses

• pcase

``````(pcase (list 1 2)
((and `(,a ,b) (guard (oddp a)))
"Two element list starting with an odd number")
(`(,a ,b)
"Other two element list."))
``````

``````(match (list 1 2)
((list (? #'oddp a) b)
"Two element list starting with an odd number")
((list a b)
"Other two element list."))
``````

pcase-let vs match-let

• pcase

``````(pcase-let ((`(,x ,y) (list 0 0)))
(cons x y))
``````

``````(match-let (((list x y) (list 0 0)))
(cons x y))
``````

``````(match-let (((list x y) (list 0 0)))
(if (< (+ x y) 100)
(recur (list (+ x 1) (+ y x)))
(list x y)))  ;; => (14 91)
``````

defun-match

``````(defun-match- product (nil)
"The empty product."
1)

(defun-match product (nil acc)
"Recursion termination."
acc)

(defun-match product ((cons (p #'numberp n) (p #'listp rest))
(p #'numberp acc))
"Main body of the product function."
(message "%S" (list :n n :rest rest :acc acc))
(recur rest (* n acc)))

(defun-match product (lst)
"Calculate the product of the numbers in LST."
(recur lst 1))
``````

``````product
product-(lst)
product-(nil)
product-(nil acc)
product-((cons (p #'numberp n) (p #'listp rest)) (p #'numberp acc))
``````

``````(product nil)            ;; => 1
(product nil 2)          ;; => 2
(product '(3 . (4 5)) 1) ;; => 60
(product '(3 4 5))       ;; => 60
``````

``````(cons <PATTERN-1> <PATTERN-2>)
``````

``````(defun foop (pair)
(string= (car pair) "foo"))

(defpattern foop (car cdr)
`(? #'foop (cons ,car ,cdr)))

(match (cons "foo" 1)
((foop _ v) v) (_ nil)) ;; => 1

(match (cons "bar" 2)
((foop _ v) v) (_ nil)) ;; => nil
``````

Refs

5 个赞

pcase 用 ``` (Backquote-Style Patterns) 也很直观：

``````(pcase '(1 2)
(`(,(pred oddp) ,_) "Match"))
;; => "Match"
``````
1 个赞

``````(match "bobcatdog"
((concat (and (or "bobcat" "cat") which) "dog")
which)) ;; => "bobcat"
``````

``````(pcase '(1 2)
(`(,(and (pred oddp) x) ,_) x))
;; => 1
``````

`rx` 有支持 pcase

``````(pcase "bobcatdog"
((rx (let which (or "bobcat" "cat")) "dog") which))
;; => "bobcat"
``````

1 个赞

``````(pcase "2019-02-27"
((rx (let year  (= 4 digit)) "-"
(let month (= 2 digit)) "-"
(let day   (= 2 digit)))
(list year month day)))
;; => ("2019" "02" "27")
``````
2 个赞

pcase + rx 的代码确实紧凑，展开之后可读性也好。而 shadchen 展开之后是一大坨，而且 concat 还有 bug，待会去提个 issue，看作者还有没有在维护。

pcase & rx 这样的包，虽然是随着 Emacs 一起发布的，但其实对 Emacs 本身没什么依赖，刚才把 pcase-tests.el 和 rx-tests.el 在低版本的 Emacs 上跑了一遍，就只缺少这三个变量／函数，从 subr.el 抄过来，然后测试用例就全过了：

``````gensym-counter      (var)
gensym              (fn)
define-symbol-prop  (fn)
``````

UPDATE

``````(match "2019-02-27"
((concat year "-" month "-" day)
(list year month day))) ;; => ("02" "2019" "27") <-- BUG？

(match "2019-02-27"
((concat year "-" (concat month "-" day))
(list year month day))) ;; => ("2019" "02" "27")
``````

http://www.ccs.neu.edu/home/shivers/papers/sre.txt

CL的PCRE正则实现，包含一个类rx的基于Sexp描述正则的玩意。

pcase是内置宏，所以有很多配套工具

``````;; seq.el
(pcase [1 2 3 4 5 6 7 8 9]
;; Each pattern will match each elem, use `&rest' to match the rest part.
((seq (and a (pred (= 1))) b c d &rest rest)
(list a b c d rest)))
;; => (1 2 3 4 [5 6 7 8 9])

;; cl-macs.el
(cl-defstruct human
name age gender)
(pcase (make-human :name "Foo" :age 20 :gender 'male)
;; (cl-struct struct-name (property-name pattern) ...)
((cl-struct human
;; (name (pred (equal "Foo"))) will not catch the variable `name'
;; Don't know why
(name (and name (pred (equal "Foo")))) age gender)
(list name age gender)))
;; => ("Foo" 20 male)
``````

1 个赞

pcase 其实也早在 2010 年就有了(GIT:emacs-lisp/pcase.el:3621208)，但也一直在进化，所以同一份代码，在不同版本的 Emacs 下表现可能会不同，例如：

``````(pcase '(1 2 3)
(`(1 2 ,(and foo (let (or 3 4) foo)))
foo)) ;; => 3
``````

``````(pcase '(1 2 3)
;; (`(1 2 ,(and foo (pred (lambda (n) (memq n '(3 4))))))
(`(1 2 ,(and foo (XXX (memq '(3 4)))))
foo)) ;; => 3
``````

`pcase``let``rx``let` 含义本身就不同，前者是直接由 `pcase` 处理，后者需先由 `pcase``rx` 支持代码处理。

• `pcase``let` 的解释是：`(let PAT EXPR)`，也就是执行 `EXPR`，然后用得到的返回值来让 `PAT` 来匹配。所以我把它理解成重新做一次匹配，所以 `(let foo (+ 1 2))` 能够匹配且 `foo` 为 3，是因为在 `pcase` 中，一个符号匹配任何值；
• `rx` 中的 `let` 貌似可以理解成变量赋值。

``````(pcase '(1 2 3)
(`(1 2 ,(and (or 3 4) foo)) foo))
;; => 3
``````

``````(pcase '(1 2 3)
(`(1 2 ,(and foo (guard (memq foo '(3 4)))))
foo))
;; => 3
``````
1 个赞

`pcase--flip` 貌似可行：

``````(pcase 3
((pred (pcase--flip memq '(1 2 3 4 5)))
"Match"))
;; => "Match"
``````

``````(pcase 3
((pred (lambda (x) (memq x '(1 2 3 4 5))))
"Match"))
;; => "Match"
``````

``````(pcase "FOO"
((pred (pcase--flip assoc
'(("foo" . 1) ("bar" . 2))
(lambda (s1 s2)
(s-equals? (s-upcase s1) (s-upcase s2)))))
"Match"))
;; => macroexpand: Wrong number of arguments: (3 . 3), 4
``````

pcase 扩展性略显欠缺

pcase当然有扩展性，上面我提到的struct和seq的匹配都不是pcase里写死的。

``````ELISP> (pcase-defmacro pred-first (pat)
(let ((arg (make-symbol "argname")))
`(pred (lambda (,arg) (,(car pat) ,arg ,@(cdr pat))))))
pred-first--pcase-macroexpander
ELISP> (pcase 'a
((and a (pred-first (memq '(a b c))))
(list a)))
(a)
``````
1 个赞

APPEND

``````lisp/emacs-lisp/cl-macs.el 3005:        (pcase-defmacro cl-struct (type &rest fields)
lisp/emacs-lisp/eieio.el 348:           (pcase-defmacro eieio (&rest fields)
lisp/emacs-lisp/map.el 49:              (pcase-defmacro map (&rest args)
lisp/emacs-lisp/rx.el 1192:             (pcase-defmacro rx (&rest regexps)
lisp/emacs-lisp/seq.el 74:              (pcase-defmacro seq (&rest patterns)
test/lisp/emacs-lisp/pcase-tests.el 37: (pcase-defmacro pcase-tests-plus (pat n)
``````

Fallthrough

``````switch (expr) {
case 0:
case 1:
// do something
break;
}
``````

• pcase
``````(pcase '("foo" "bar")
((or `("foo" ,_)
`(,_ "bar")) t))
;; =>
;; (pcase it
;;   (`("foo" ,_) t)
;;   (`(,_ "bar") t))
;; => t
``````
``````(match '("foo" "bar")
((or (list "foo" _)
(list _ "bar")) t)
(_ nil))
;; =>
;; (match '("bar" "foo")
;;   ((list "foo" _) t)
;;   ((list _ "bar") t)
;;   (_ nil))
;; => t
``````

EDIT：经楼下提醒，去除了 pcase 例子中多余的 ``,`

``````(pcase '("foo" "bar")
((or `("foo" ,_)
`(,_ "bar"))
t))
``````

1 个赞