2014-09-29 16 views
8

Pomyślałem, że odkąd Emacs Lisp i Common Lisp wydawały się tak blisko spokrewnione ze składnią, mogłem po prostu zastosować przykładowy kod, który znalazłem na RosettaCode, ale okazało się, że się myliłem.Jak utworzyć nazwane argumenty w ELisp?

Kod w pytaniu wygląda następująco:

(defun print-name (&key first (last "?")) 
    (princ last) 
    (when first 
    (princ ", ") 
    (princ first)) 
    (values)) 

i zgodnie RosettaCode należy wykonać następujące czynności:

> (print-name) 
    ? 
> (print-name :first "John") 
    ?, John 
> (print-name :last "Doe") 
    Doe 
> (print-name :first "John" :last "Doe") 
    Doe, John 

Teraz tutaj jest rzeczą; gdy próbuję uruchomić tę funkcję w moim Elisp tłumacza, pojawia się następujący błąd:

*** Eval error *** Wrong number of arguments: (lambda (&key first (last "?")) (princ la\ 
st) (if first (progn (princ ", ") (princ first))) (values)), 0 

nie jestem routined tyle w Lisp, aby wiedzieć, co to miało znaczyć, a nie ilość googlowania ma przewagę mnie bliższy odpowiedzi.

Więc jaki jest właściwy sposób robienia tego w Emacs Lisp?

Odpowiedz

2

Niestety elispdoes not support named arguments per-se. Można jednak emulować tę funkcję, przekazując alist do funkcji, check this for a guide on alists.

Sednem oznacza to, że można przejść do mapa podobną strukturę danych do funkcji, dlatego trzeba zadbać zarówno opakowania (skład argumentów do alist) i rozpakowaniu (rozkładowi do zmiennych/kontroli wymaganych/opcjonalnych wartości).

Tworzenie struktury wejściowego jest proste, tylko komponować CONS komórki zmiennej symbolu i jego wartość:

(print-name '((first . "John") (last . "Doe"))) 

czytanie jest jeszcze prostsze: wystarczy użyć assoc aby uzyskać odpowiednią wartość:

(assoc 'last '((first . "John") (last . "Doe"))) 
; => (last . "Doe") 
(assoc 'middle '((first . "John") (last . "Doe"))) 
; => nil 

Stąd print-name mógłby wyglądać następująco:

(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

Idiomatically, wolisz używać 'rekreacyjne args' i analizować' args' jako plist, który ma mniej bałaganu składniowej: '(druk -name: pierwszy "John": ostatni "Doe") '. – lunaryorn

12

Elisp's defun nie obsługuje &key (obsługuje też &optional i &rest). Istnieje makro umożliwiające definiowanie funkcji za pomocą &key. W Emacs 24.3, a później można wymagać cl-lib i używać cl-defun:

(require 'cl-lib) 
(cl-defun print-name (&key first (last "?")) 
    ... 

We wcześniejszych wersjach Emacsa, ta sama funkcjonalność jest w bibliotece cl pod nazwą defun*:

(require 'cl) 
(defun* print-name (&key first (last "?")) 
    ... 

Biblioteka cl-lib jest korzystne do cl, ponieważ utrzymuje porządek przestrzeni nazw przez prefiksowanie wszystkich symboli za pomocą cl-; Jeśli jednak potrzebujesz kompatybilności z wcześniejszymi wersjami Emacsa, możesz preferować cl i defun*.


Funkcja w tym przykładzie zawiera również wywołanie funkcji values. Ta funkcja jest charakterystyczna dla Common Lisp, ale jest dostępna jako cl-values w cl-lib i values w cl.

Jednak alternatywy nie działają dokładnie tak samo, jak ich odpowiednik w Common Lisp. Common Lisp ma pojęcie wielokrotnych wartości zwracanych. Na przykład wywołanie (values 1 2 3) zwróci trzy wartości - a wywołanie (values) jak wyżej zwróci wartości zerowe. Funkcje Emacs Lisp emulują wiele wartości zwracanych za pomocą list, a także przedefiniowują dopasowanie do multiple-value-bind. Oznacza to, że wywołanie (cl-values) zwróci tylko nil (pusta lista).

W takiej funkcji Emacs Lisp można albo wyraźnie określić wartość zwracaną jako nil, albo po prostu ją całkowicie pominąć, pozostawiając wartość zwracaną ostatniej formy jako wartość zwracaną funkcji, ponieważ wywołujący jest nie oczekuje się użycia wartości zwracanej.

+0

To nie działa dokładnie, pojawia się komunikat "Definicja funkcji symbolu jest nieważna: wartości" –

+0

W prawo, funkcja 'values' jest kolejną specjalnością Common Lisp. Nazywa się 'cl-values' w' cl-lib'. – legoscia

+0

Wykonanie tego tylko powoduje, że zwraca zero, a nie drukuje cokolwiek ... Przynajmniej nie powoduje błędu –

12

Od Emacs Lisp nie obsługuje bezpośrednio argumentów słów kluczowych, trzeba naśladować nich, albo z cl-defun jak w innych odpowiedzi, lub analizowania argumentów jako 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 mówi Emacs wstaw wszystkie argumenty funkcji do jednej listy. plist-get wyodrębnia wartość z listy właściwości, to znaczy listę formatu (key1 value1 key2 value2 …). W rzeczywistości paczka jest spłaszczonym alistem.

Wraz ten pozwala nazwać print-name podobnie jak w pytaniu:

> (print-name) 
    ? 
> (print-name :first "John") 
    ?, John 
> (print-name :last "Doe") 
    Doe 
> (print-name :first "John" :last "Doe") 
    Doe, John 
Powiązane problemy