Emacs 自带 Destructuring binding API


#1

试着整理了 Emacs (25) 中能用来 Destructuring binding 的工具:

Library API Scope
cl-lib cl-destructuring-bind List
pcase pcase, pcase-let List, Vector, Etc
seq seq-let List, Vector, String
let-alist let-alist Alist
map map-let Alist, Hash Table, Vector

以上都是 Emacs 25 自带,可以从所支持的数据类型 (List, Alist, Hash-Table 等) 这个角度比较它们。


1. cl-destructuring-bind (List)

(cl-destructuring-bind (x y) '(1 2)
  (list x y))
;; => (1 2)

(cl-destructuring-bind ((k1 . v1) (k2 . v2)) '((:one . 1) (:two . 2) )
  (list k1 v1 k2 v2))
;; => (:one 1 :two 2)

(cl-destructuring-bind (k1 v1 k2 v2) '(:one 1 :two 2)
  (list k1 v1 k2 v2))
;; => (:one 1 :two 2)

2. pcase-let (List, Vector, Etc)

(pcase-let ((`(,x ,y)
             '(1 2)))
  (list x y))
;; => (1 2)

(pcase-let ((`((,k1 . ,v1) (,k2 . ,v2))
             '((:one . 1) (:two . 2))))
  (list k1 v1 k2 v2))
;; => (:one 1 :two 2)

(pcase-let ((`(,k1 ,v1 ,k2 ,v2)
             '(:one 1 :two 2)))
  (list k1 v1 k2 v2))
;; => (:one 1 :two 2)

(pcase-let ((`[,x ,y] '[1 2]))
  (list x y))
;; => (1 2)

3. seq-let (List, Vector, String)

(seq-let (x y) '(1 2)
  (list x y))
;; => (1 2)

(seq-let [x y] [1 2]
  (list x y))
;; => (1 2)

(seq-let [x y] "Hello World"
  (string x y))
;; => "He"

(seq-let ((k1 . v1) (k2 . v2)) '((:one . 1) (:two . 2))
  (list k1 v1 k2 v2))
;; error-> Wrong type argument: listp, v1
;; 不支持

(seq-let ((x y)) '((1 2))
  (list x y))
;; => (1 2)

4. let-alist (Alist)

(let-alist '((one . 1) (two . 2))
  (list .one .two))
;; => (1 2)

5. map-let (Alist, Hash Table & Array)

(map-let (one two) '((one . 1) (two . 2))
  (list one two))
;; => (1 2)

(map-let ((:one one) (:two two)) '((:one . 1) (:two . 2))
  (list one two))
;; => (1 2)

(map-let (one two) #s(hash-table data (one 1 two 2 three 3))
  (list one two))
;; => (1 2)

(map-let ((1 one) (99 ninety-nine)) (vconcat (number-sequence 0 100))
  (list one ninety-nine))
;; => (1 99)

#2

是不是写错了?前后都有空格的.才表示cons cell吧?和其他字符连在一起时只是一个字符,比如.one整个是一个symbol?


#3

不是,那是 let-alist 的写法,获取嵌套的 Alist 会很方便

(let-alist '((one . ((two . ((three . 4))))))
  .one.two.three)
;; => 4

相信看下 C-h f let-alist 然后试一下就能明白。


#4

dash 也提供了一些类似的函数:

(-let (([a (b c) d] [1 (2 3) 4]))
  (list a b c d)) ;; => '(1 2 3 4)

(-let [(a b c . d) (list 1 2 3 4 5 6)]
  (list a b c d)) ;; => '(1 2 3 (4 5 6))

(-let [(&plist :foo foo :bar bar) (list :baz 3 :foo 1 :qux 4 :bar 2)]
  (list foo bar)) ;; => '(1 2)

(-let* (((a . b) (cons 1 2))
        ((c . d) (cons 3 4)))
  (list a b c d)) ;; => '(1 2 3 4)

(-let* (((a . b) (cons 1 (cons 2 3)))
        ((c . d) b))
  (list a b c d)) ;; => '(1 (2 . 3) 2 3)

(-let* (((&alist "foo" foo "bar" bar) (list (cons "foo" 1) (cons "bar" (list 'a 'b 'c))))
        ((a b c) bar))
  (list foo a b c bar)) ;; => '(1 a b c (a b c))

(-map (-lambda ((x y)) (+ x y)) '((1 2) (3 4) (5 6))) ;; => '(3 7 11)
(-map (-lambda ([x y]) (+ x y)) '([1 2] [3 4] [5 6])) ;; => '(3 7 11)
(funcall (-lambda ((_ . a) (_ . b)) (-concat a b)) '(1 2 3) '(4 5 6)) ;; => '(2 3 5 6)

(progn (-setq a 1) a) ;; => 1
(progn (-setq (a b) (list 1 2)) (list a b)) ;; => '(1 2)
(progn (-setq (&plist :c c) (list :c "c")) c) ;; => "c"