我认为既然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中没有足够的常规来知道这意味着什么,并且没有任何 Google搜索让我更接近答案。
那么在Emacs Lisp中这样做的正确方法是什么?
答案 0 :(得分:16)
由于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
答案 1 :(得分:13)
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
的{{1}}库中:
defun*
(require 'cl)
(defun* print-name (&key first (last "?"))
...
库优先于cl-lib
,因为它通过在cl
前添加所有符号来保持名称空间整洁;但是,如果您需要与早期的Emacs版本兼容,则可能更喜欢cl-
和cl
。
示例中的函数还包含对函数defun*
的调用。此函数特定于Common Lisp,但values
中的cl-values
和cl-lib
中的values
可用。
然而,替代方案与Common Lisp对应方式完全不同。 Common Lisp具有多个返回值的概念。例如,调用cl
将返回三个值 - 并且如上所述调用(values 1 2 3)
将返回零值。 Emacs Lisp函数通过列表模拟多个返回值,并重新定义(values)
以匹配。这意味着调用multiple-value-bind
将只返回(cl-values)
(空列表)。
在这样的Emacs Lisp函数中,您要么将返回值显式指定为nil
,要么将其完全保留,将最后一个表单的返回值保留为函数的返回值,因为不希望调用者使用返回值。
答案 2 :(得分:3)
不幸的是elisp
does not support named arguments per-se。但是,您可以通过将alist
传递给函数check this for a guide on alists
来模拟该功能。
核心,这意味着您将类似地图的数据结构传递到函数中,因此您需要处理两种包装(将参数组合到alist
中)并自行解包(分解成变量/检查所需/可选值)。
创建输入结构很简单,只需构成变量符号的cons单元及其值:
(print-name '((first . "John") (last . "Doe")))
阅读更简单:只需使用assoc
获取相应的值:
(assoc 'last '((first . "John") (last . "Doe")))
; => (last . "Doe")
(assoc 'middle '((first . "John") (last . "Doe")))
; => nil
因此,print-name
可能如下所示:
(defun print-name (alist)
(let ((first (assoc 'first alist))
(last (assoc 'last alist)))
(if last
(princ (cdr last))
(error "Missing last name!"))
(when first
(princ ", ")
(princ (cdr first)))))