我认为既然Emacs Lisp和Common Lisp在语法方面看起来非常相似,我可以按照我在RosettaCode上找到的示例代码,但事实证明我错了.
有问题的代码如下所示:
(defun print-name (&key first (last "?")) (princ last) (when first (princ ", ") (princ first)) (values))
根据RosettaCode,它应该执行以下操作:
> (print-name) ? > (print-name :first "John") ?, John > (print-name :last "Doe") Doe > (print-name :first "John" :last "Doe") Doe, John
现在,这就是事情; 每当我尝试在我的ELisp解释器中运行该函数时,我都会收到以下错误:
*** Eval error *** Wrong number of arguments: (lambda (&key first (last "?")) (princ la\ st) (if first (progn (princ ", ") (princ first))) (values)), 0
我在lisp中没有足够的常规来知道这应该是什么意思,并且没有任何谷歌搜索让我更接近答案.
那么在Emacs Lisp中这样做的正确方法是什么?
由于Emacs Lisp不直接支持关键字参数,因此您需要模拟这些参数,或者使用cl-defun
其他答案,或者将参数解析为plist:
(defun print-name (&rest args) (let ((first (plist-get args :first)) (last (or (plist-get args :last) "?"))) (princ last) (when first (princ ", ") (princ first))))
&rest args
告诉Emacs将所有函数参数放入一个列表中. plist-get
从属性列表中提取值,即格式列表(key1 value1 key2 value2 …)
.实际上,plist是扁平的alist.
这可以让你print-name
像在你的问题中一样打电话:
> (print-name) ? > (print-name :first "John") ?, John > (print-name :last "Doe") Doe > (print-name :first "John" :last "Doe") Doe, John
elisp的的defun
不支持&key
(它不支持&optional
和&rest
,虽然).有一个宏可以让你使用定义函数&key
.在Emacs 24.3及更高版本中,您可以要求cl-lib
并使用cl-defun
:
(require 'cl-lib) (cl-defun print-name (&key first (last "?")) ...
在早期的Emacs版本中,相同的功能在cl
库中的名称下defun*
:
(require 'cl) (defun* print-name (&key first (last "?")) ...
该cl-lib
库是首选cl
,因为它通过在所有符号前加上来保持名称空间整洁cl-
; 但是,如果您需要与早期的Emacs版本兼容,您可能更喜欢cl
和defun*
.
示例中的函数还包含对函数的调用values
.此函数特定于Common Lisp,但可以在cl-values
in cl-lib
和values
in中使用cl
.
但是,替代方案与Common Lisp对应方法的工作方式完全不同.Common Lisp具有多个返回值的概念.例如,调用(values 1 2 3)
将返回三个值 - 并且(values)
如上所述调用将返回零值.Emacs Lisp函数通过列表模拟多个返回值,并重新定义multiple-value-bind
匹配.这意味着调用(cl-values)
将返回nil
(空列表).
在这样的Emacs Lisp函数中,您要么显式指定返回值nil
,要么完全不将它保留,将最后一个表单的返回值保留为函数的返回值,因为调用者不应该使用回报价值.