2014-09-29 73 views
8

我想,由于Emacs Lisp和Common Lisp看起来如此紧密相关,所以我可以按照我在RosettaCode上找到的示例代码进行操作,但事实证明我错了。如何在ELisp中创建命名参数?

有问题的代码如下所示:

(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 

我不routined足够口齿不清知道什么是应该的意思是,和谷歌搜索有铅无量我更接近答案。

那么在Emacs Lisp中这样做的正确方法是什么?

回答

2

不幸的是elispdoes not support named arguments per-se。但是,您可以通过将alist传递给函数check this for a guide on alists来模拟该功能。

在核心,这意味着您传递地图状数据结构到功能,因此需要照顾两个包装的(的参数组合物引入alist)和展开(分解成变量/检查必需/可选值)。

创建输入结构的简单,只是组成缺点和可变码元的细胞中的值:

(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))))) 
+5

惯用,你想用'&休息args'和分析'args'为plist中,具有较少语法混乱:'(打印-name:第一个“John”:最后一个“Doe”)'。 – lunaryorn 2014-09-29 15:56:31

12

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版本兼容,您可能更喜欢cldefun*


该示例中的函数还包含对函数values的调用。此功能特定于Common Lisp,但可用cl-valuescl-libvaluescl

但是,这些替代方法与Common Lisp的对应方式不一样。 Common Lisp具有多个返回值的概念。例如,调用(values 1 2 3)将返回三个值 - 如上所述调用(values)将返回零值。 Emacs Lisp函数通过列表模拟多个返回值,并重新定义multiple-value-bind以匹配。这意味着电话(cl-values)将返回nil(空列表)。

在这样一个Emacs Lisp函数中,您要么明确地指定返回值为nil,要么将其全部保留,留下最后一个形式的返回值作为函数的返回值,因为调用者是不期望使用返回值。

+0

这不正确的工作,我收到一个错误,说'符号的功能定义是无效的:价值' – 2014-09-29 16:18:56

+0

正确的说,'价值'功能是另一个Common Lisp专业。它在'cl-lib'中被称为'cl-values'。 – legoscia 2014-09-29 16:20:42

+0

这样做只是使它返回零而不是打印任何东西......至少它不会产生错误 – 2014-09-29 17:20:17

12

由于的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