博客原文地址: http://liu233w.github.io/blog/2016/09/02/multiple-micro-state/
只要贴个链接就可以了,不要把同一个内容复制粘贴过来。不然的话,你和读者会面临两个版本的内容、两个讨论环境等等,比如这里的 TOC 并不能跳转。
不错。我也是试了下。但是不需要接受函数的话,可以直接用 bind-key.el 的 bind-keys
。
(defmacro jerry-bind-keys (map &rest bindings)
(declare (indent 1))
(let ((forms
(mapcar (lambda (binding)
`(define-key ,map ,(car binding) #',(cdr binding)))
bindings)))
`(progn ,@forms)))
(jerry-bind-keys global-map
("\M-1" . emacs-version)
("\M-2" . emacs-init-time))
==> (progn
(define-key global-map "\261"
(function emacs-version))
(define-key global-map "\262"
(function emacs-init-time)))
对,重定义一个命令会有用,但是参数列表可能会有问题。
哈,根据 bytecode 的话,参数表也能获得。
动态环境下,参数表能直接获得
(byte-compile (lambda (a b &optional c &rest r)))
=> #[(a b &optional c &rest r) "\300\207" [nil] 1]
静态环境下,能获得参数表的个数(也就是:必要参数个数,可选参数个数和是否含 &rest
)
(setq lexical-binding t)
(byte-compile (lambda (a b &optional c &rest r)))
=> #[898 "\300\207" [nil] 5 "
(fn A B &optional C &rest R)"]
把参数描述 898
改成 16 进制:
(format "%x" 898)
=> "382"
再对应这个表
0 6 7 8 14 15
xxxx, xxx, x, xxxx, xxx, x
^ ^ ^ ^ ^
--------- - ---------
min &rest max
可以发现,最多有 3 个参数,最少有 3 个参数,含有 &rest
,所以可以推断出函数的参数必然形如:(var1 var2 &optional var3 &rest rest)
Emacs Bytecode 的文档:info ‘(elisp) Byte-Code Objects’ 或 Byte-Code Objects (GNU Emacs Lisp Reference Manual)
emacs的describe-function
产生的帮助说明里面是有函数的所有参数的。我感觉可以研究这个来找到办法。
我刚刚发现了一个很奇怪的特性,如果使用函数documentation
来返回函数的文档的话,在文档的末尾会有函数的声明。比如:
(documentation 'describe-function)
=>
"Display the full documentation of FUNCTION (a symbol).
(fn FUNCTION)"
而那个函数内部的文档底下是没有这个东西的。
但是如果我自己定义了一个函数,就没有这种东西。
documentation
是一个C语言函数,看不了elisp代码。我也不知道这是什么原理了。
EDIT
我刚才生成了一个词法作用域的函数试了一下。 好像只有在词法作用域之下编译之后的函数才会在document的最后生成函数的声明。 如果是动态作用域就只能从byte-compile里面得到声明了。
是
这时候 documentation
就不行了。用 Emacs 25 的 elisp-get-fnsym-args-string
应该动态、静态都可以:
(elisp-get-fnsym-args-string 'next-line)
=> #("next-line: (&optional ARG TRY-VSCROLL)" 0 9 (face font-lock-function-name-face))
用 Byte code 的话,静态作用域下定义的函数的名字信息就没有了,这是为什么 lexical-binding 快的原因之一 [1]。我刚刚写了个 arglist-form
,就用的是 Byte code。
https://github.com/xuchunyang/learn/commit/12540e9560c7339a3838cc44caef113f88c03421
(arglist-form #'emacs-version)
=> (&optional here)
(setq lexical-binding t)
(arglist-form '(lambda (_a _b &optional _c &rest _r)))
=> (a b &optional c &rest others)
- Emacs Byte-code Internals: Emacs Byte-code Internals
我觉得可以直接使用read
来将byte-compile
的返回值变成列表,比如:
(setf lexical-binding t)
(defun test1 (arg1 arg2 arg3 &optional arg4))
(read
(replace-regexp-in-string
"^.*\n\\|]$" "" (format "%s" (byte-compile 'test1))))
=>
(fn ARG1 ARG2 ARG3 &optional ARG4)
这样就可以用函数原来的参数名了
还有一个问题,貌似describe-function
没有使用byte-compile
,但是它是怎么获得函数的参数的呢?
是个办法,
看起来是:
(documentation #'next-line t)
我没发现参数表有什么作用,知道大概怎么回事就完了,具体细节也无所谓。与之相比,Emacs 的 Byte-code 应该更有趣:可以设计一门语言,解释或编译成 Emacs byte-code,然后就能用那种语言扩展 Emacs 了,不一定有什么实际价值,但是一定会非常有趣。
我找到了
(help-function-arglist 'test1)
=>
(arg1 arg2 arg3 &optional arg4)
_(:зゝ∠)_
这个函数里面用了一个indirect-function
,如果函数没有编译的话,可以直接返回函数的完整定义,否则会返回一个编译之后的函数对象(就像byte-compile的返回值一样)
EDIT:
我是不是绕了个弯啊,command-execute
可以直接执行interactive,那我生成的函数就不需要原来的参数了啊_(:зゝ∠)_