(info "(elisp) Generators")
以无限长的斐波那契数列为例,分别用 Python 和 Emacs Lisp 的生成器实现:
Python
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 前 10 个斐波那契数
for i, n in enumerate(fibonacci()):
if i == 10: break
print(n, end=" ")
# 0 1 1 2 3 5 8 13 21 34
Emacs Lisp
(iter-defun fibonacci ()
(let ((a 0)
(b 1))
(while t
(iter-yield a)
(cl-psetq a b
b (+ a b)))))
;; 前 10 个斐波那契数
(cl-loop repeat 10
for n iter-by (fibonacci)
collect n)
;; => (0 1 1 2 3 5 8 13 21 34)
6 个赞
额 报错了
1 Debugger entered--Lisp error: (cl-assertion-failed (lexical-binding nil))
2 cl--assertion-failed(lexical-binding)
3 #[642 "^H\204^H^@\301\300!\210\302^A!\211@^AA\303^F^F^F^F\304^E\305^F^F!C\"BBB\207" [lexical-binding cl--assertion-failed macroexp-parse-body defun append cps-generate-evaluator] 13 ("/usr/share/emacs/25.3/l$
4 macroexpand((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))) nil)
5 macroexp-macroexpand((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))) nil)
6 macroexp--expand-all((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
7 macroexpand-all((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
8 eval-sexp-add-defvars((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
9 elisp--eval-last-sexp(nil)
10 eval-last-sexp(nil)
11 funcall-interactively(eval-last-sexp nil)
12 call-interactively(eval-last-sexp nil nil)
13 command-execute(eval-last-sexp)
14 recursive-edit()
15 debug(error (cl-assertion-failed (lexical-binding nil)))
16 cl--assertion-failed(lexical-binding)
17 #[642 "^H\204^H^@\301\300!\210\302^A!\211@^AA\303^F^F^F^F\304^E\305^F^F!C\"BBB\207" [lexical-binding cl--assertion-failed macroexp-parse-body defun append cps-generate-evaluator] 13 ("/usr/share/emacs/25.3/l$
18 macroexpand((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))) nil)
19 macroexp-macroexpand((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))) nil)
20 macroexp--expand-all((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
21 macroexpand-all((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
22 eval-sexp-add-defvars((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
23 elisp--eval-last-sexp(nil)
24 eval-last-sexp(nil)
25 funcall-interactively(eval-last-sexp nil)
26 call-interactively(eval-last-sexp nil nil)
27 command-execute(eval-last-sexp)
28 recursive-edit()
29 debug(error (cl-assertion-failed (lexical-binding nil)))
30 cl--assertion-failed(lexical-binding)
31 #[642 "^H\204^H^@\301\300!\210\302^A!\211@^AA\303^F^F^F^F\304^E\305^F^F!C\"BBB\207" [lexical-binding cl--assertion-failed macroexp-parse-body defun append cps-generate-evaluator] 13 ("/usr/share/emacs/25.3/l$
32 macroexpand((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))) nil)
33 macroexp-macroexpand((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))) nil)
34 macroexp--expand-all((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
35 macroexpand-all((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
36 eval-sexp-add-defvars((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
37 elisp--eval-last-sexp(nil)
38 eval-last-sexp(nil)
39 funcall-interactively(eval-last-sexp nil)
40 call-interactively(eval-last-sexp nil nil)
41 command-execute(eval-last-sexp)
42 recursive-edit()
43 debug(error (cl-assertion-failed (lexical-binding nil)))
44 cl--assertion-failed(lexical-binding)
45 #[642 "^H\204^H^@\301\300!\210\302^A!\211@^AA\303^F^F^F^F\304^E\305^F^F!C\"BBB\207" [lexical-binding cl--assertion-failed macroexp-parse-body defun append cps-generate-evaluator] 13 ("/usr/share/emacs/25.3/l$
46 macroexpand((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))) nil)
47 macroexp-macroexpand((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))) nil)
48 macroexp--expand-all((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
49 macroexpand-all((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
50 eval-sexp-add-defvars((iter-defun fib nil (let ((a 0) (b 1)) (while t (iter-yield a) (cl-psetq a b b (+ a b))))))
51 elisp--eval-last-sexp(nil)
52 eval-last-sexp(nil)
53 funcall-interactively(eval-last-sexp nil)
54 call-interactively(eval-last-sexp nil nil)
55 command-execute(eval-last-sexp)
56 recursive-edit()
57 debug(error (cl-assertion-failed (lexical-binding nil)))
58 cl--assertion-failed(lexical-binding)
59 #[642 "^H\204^H^@\301\300!\210\302^A!\211@^AA\303^F^F^F^F\304^E\305^F^F!C\"BBB\207" [lexical-binding cl--assertion-failed ma
code:
(require 'generator)
(iter-defun fib ()
(let ((a 0)
(b 1))
(while t
(iter-yield a)
(cl-psetq a b
b (+ a b)))))
lexical-binding 设为 t
在文首:
;;; test-fibonacci.el --- Test fibonacci -*- lexical-binding: t; -*-
或者
(let ((lexical-binding t))
;; do something
)
把 iter-defun
展开看了一下,好家伙,100 多行,20 多个 lambda。
然后再对比其它实现,各做了 1000 次的 fibonacci(100)
测试,生成器在书写上最直观,但效率是最低的:
⋊> emacs-nightly --batch -l ~/.scratch/elisp/test-fibonacci.el
fibonacci1 [generator]: 0.847885s
fibonacci2 [iteration]: 0.319813s
fibonacci3 [recursion]: 0.565710s
fibonacci4 [generat0r]: 0.175119s
不过这测试也不完全公平,因为其中的递归版本 fibonacci3 没有收集计算过程中的值。循环版本 fibonacci2 增加了回调以收集计算过程中的值,算是稍微增加了一点公平性。fibonacci4 则是模拟生成器效果的一个实现:
(defvar fibonacci4--a 0)
(defvar fibonacci4--b 1)
(defun fibonacci4 ()
(let ((a fibonacci4--a))
(setq fibonacci4--a fibonacci4--b
fibonacci4--b (+ a fibonacci4--b))
a))
(dotimes (_ 1000)
(setq fibonacci4--a 0)
(setq fibonacci4--b 1)
(cl-loop repeat 100
for n = (fibonacci4)
collect n))
完整测试代码:Test fibonacci #emacs-lisp · GitHub
1 个赞
除了 @twlz0ne 提到的办法,一般测试时,在需要的 Buffer (对我来说一般是 *scratch*
)中 (setq lexical-binding t)
也行。
(setq lexical-binding nil)
;; => nil
(let ((x 123)) (defun foo () x))
;; => foo
(foo)
error-> Symbol's value as variable is void: x
(setq lexical-binding t)
;; => t
(let ((x 123)) (defun foo () x))
;; => foo
(foo)
;; => 123
哎,少加了个q……
(setq lexical-binding t)
ok了