2014-09-29 2 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: первый «Джон»: последний «Doe»). – lunaryorn

12

Elisp's 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 в cl-lib и values в cl.

Однако альтернативы не работают точно так же, как их общий аналог Lisp. Common Lisp имеет концепцию множественных возвращаемых значений. Например, вызов (values 1 2 3) вернет три значения - и вызов (values), как указано выше, вернет нулевые значения. Функции Emacs Lisp эмулируют несколько возвращаемых значений посредством списков, а также переопределяют multiple-value-bind для соответствия. Это означает, что вызов (cl-values) просто вернет nil (пустой список).

В такой функции Emacs Lisp вы либо явно указываете возвращаемое значение как nil, либо просто оставьте его полностью, оставив возвращаемое значение последней формы в качестве возвращаемого значения функции, поскольку вызывающий не предполагается использовать возвращаемое значение.

+0

Это не работает, я получаю сообщение об ошибке: «Определение функции символа является void: values» –

+0

Правильно, функция 'values' - это еще одна специальность Common Lisp. Он называется 'cl-values' в' cl-lib'. – legoscia

+0

Выполнение этого просто заставляет его возвращать нуль, а не печатать что-либо ... По крайней мере, это не приводит к ошибке –

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 является сплюснутым алистом.

вместе это позволяет назвать print-name так же, как в вашем вопросе:

> (print-name) 
    ? 
> (print-name :first "John") 
    ?, John 
> (print-name :last "Doe") 
    Doe 
> (print-name :first "John" :last "Doe") 
    Doe, John 
Смежные вопросы