Emacs 如何在ELisp中生成命名参数?
我想既然Emacs-Lisp和Common-Lisp在语法上似乎有着如此密切的关系,我可以按照我在上面找到的示例代码来做,但结果证明我错了 所讨论的代码如下所示:Emacs 如何在ELisp中生成命名参数?,emacs,elisp,Emacs,Elisp,我想既然Emacs-Lisp和Common-Lisp在语法上似乎有着如此密切的关系,我可以按照我在上面找到的示例代码来做,但结果证明我错了 所讨论的代码如下所示: (defun print-name (&key first (last "?")) (princ last) (when first (princ ", ") (princ first)) (values)) 根据RosettaCode,它应该执行以下操作: > (print-name)
(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
我没有足够的口齿不清的习惯来理解这意味着什么,再多的谷歌搜索也没有让我更接近一个答案
那么,在Emacs Lisp中执行此操作的正确方法是什么呢?不幸的是
elisp
。但是,您可以通过向函数传递alist
来模拟该功能
在核心部分,这意味着您将一个类似映射的数据结构传递到函数中,因此您需要自己处理包装(将参数组合到列表中)和展开(分解为变量/检查必需/可选值)
输入结构的创建很简单,只需组成变量符号及其值的cons单元格:
(打印名称“((第一个“约翰”)(最后一个“Doe”))
读取更简单:只需使用assoc
即可获得相应的值:
(关联“最后”(first.John)(last.Doe)))
; => (最后一个“Doe”)
(中间名)(第一个“约翰”)(最后一个“Doe”))
; => 无
因此,print name
可以如下所示:
(defun print-name (&key first (last "?"))
(princ last)
(when first
(princ ", ")
(princ first))
(values))
(定义打印名称(列表)
(让((第一名(协会第一名))
(最后一个(关联的最后一个列表)))
(如果最后一次
(普林斯(cdr最后一任))
(错误“缺少姓氏!”)
(第一次
(普林斯)
(普林斯(cdr第一(()())))
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*
示例中的函数还包含对函数值的调用。此函数特定于通用Lisp,但在cl-lib
中作为cl-values
提供,在cl
中作为values
提供
但是,这些备选方案的工作原理与它们的公共Lisp对应项并不完全相同。Common Lisp具有多个返回值的概念。例如,调用(值1 2 3)
将返回三个值,而如上调用(值)
将返回零值。Emacs Lisp函数通过列表模拟多个返回值,并重新定义多值绑定
以匹配。这意味着调用(cl值)
将只返回nil
(空列表)
在这样的Emacs Lisp函数中,您可以显式地将返回值指定为nil
,或者干脆不使用它,将最后一个表单的返回值作为函数的返回值,因为调用方不希望使用返回值。因为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
习惯用法上,您更愿意使用&rest args
和parseargs
作为plist,它具有较少的语法混乱:(打印名:first“John”:last“Doe”)
。这不完全有效,我收到一个错误,说符号的函数定义无效:value
对,values
函数是另一个常见的Lisp特性。它在cl-lib
中被称为cl-values
。。。至少它不会产生错误