Piszę funkcję, która w zasadzie przyjmuje dowolną liczbę argumentów. W praktyce należy jednak podawać parzystą liczbę argumentów, w przeciwnym razie przyniosą niepożądane rezultaty.
Oto fałszywy przykład kontekstu:
(defun my-caller (&rest args)
(while args
(call-other-function (pop args) (pop args))))
Gdy plik elisp jest kompilowany bajtowo, kompilator bajtów generuje ostrzeżenie, gdy widzi funkcję wywoływaną z niewłaściwą liczbą argumentów. Oczywiście, nigdy tak się nie stanie my-caller
, ponieważ jest zdefiniowane, aby przyjmować dowolną liczbę.
Może jednak istnieje właściwość symbolu, którą mogę ustawić, lub (declare)
formę, którą mogę dodać do jej definicji. Coś, co powiadomi użytkownika, że ta funkcja powinna mieć tylko parzystą liczbę argumentów.
- Czy istnieje sposób poinformowania kompilatora bajtów o tym ograniczeniu?
- Jeśli nie, czy jest to możliwe za pomocą makra zamiast funkcji?
elisp
byte-compilation
Malabarba
źródło
źródło
Odpowiedzi:
EDYCJA : Lepszym sposobem na zrobienie tego w ostatnich Emacsach jest zdefiniowanie makra kompilatora w celu sprawdzenia liczby argumentów. Moja oryginalna odpowiedź przy użyciu normalnego makra została zachowana poniżej, ale makro-kompilator jest lepsze, ponieważ nie zapobiega przekazaniu funkcji do
funcall
lubapply
w czasie wykonywania.W najnowszych wersjach Emacsa możesz to zrobić, definiując makro kompilatora dla swojej funkcji, która sprawdza liczbę argumentów i wyświetla ostrzeżenie (lub nawet błąd), jeśli się nie zgadza. Jedyną subtelnością jest to, że makro kompilatora powinno zwrócić oryginalny formularz wywołania funkcji bez zmian w celu oceny lub kompilacji. Odbywa się to za pomocą
&whole
argumentu i zwracania jego wartości. Można to osiągnąć w następujący sposób:Należy pamiętać, że
funcall
iapply
mogą być teraz używane, ale obejście kontroli argument makra kompilatora. Pomimo swojej nazwy, kompilator makr również wydają się być rozszerzona w toku „interpretować” poprzez ocenę C-xC-e, M-xeval-buffertak dostaniesz błędy w zakresie oceny, jak również na przygotowanie tego przykładem.Oryginalna odpowiedź brzmi:
Oto, w jaki sposób możesz wdrożyć sugestię Jordona, aby „użyć makra, które zapewni ostrzeżenia w czasie ekspansji”. Okazuje się, że jest bardzo łatwe:
Próba skompilowania powyższego w pliku nie powiedzie się (żaden
.elc
plik nie zostanie wygenerowany), z ładnym klikalnym komunikatem o błędzie w dzienniku kompilacji, informującym:Można również wymienić
(error …)
z(byte-compile-warn …)
produkować ostrzeżenie zamiast błędu, pozwalając kompilacja, aby kontynuować. (Podziękowania dla Jordona za wskazanie tego w komentarzach).Ponieważ makra są rozszerzane w czasie kompilacji, z tym sprawdzeniem nie wiąże się żadna kara w czasie wykonywania. Oczywiście nie możesz powstrzymać innych osób dzwoniących
my-caller--function
bezpośrednio, ale możesz przynajmniej reklamować ją jako funkcję „prywatną”, stosując konwencję podwójnego łącznika.Istotną wadą używania makra do tego celu jest to, że
my-caller
nie jest on już funkcją pierwszej klasy: nie można przekazać go dofuncall
lubapply
w czasie wykonywania (lub przynajmniej nie zrobi tego, czego oczekujesz). Pod tym względem to rozwiązanie nie jest tak dobre, jak możliwość deklarowania ostrzeżenia kompilatora dla prawdziwej funkcji. Oczywiście użycieapply
i tak uniemożliwiłoby sprawdzenie liczby argumentów przekazywanych do funkcji w czasie kompilacji, więc być może jest to akceptowalny kompromis.źródło
byte-compile-warn
apply
lubfuncall
jego braku . Spróbuję i zmienię odpowiedź, jeśli zadziała.Tak, możesz użyć
byte-defop-compiler
do określenia funkcji, która kompiluje twoją funkcję,byte-defop-compiler
ma pewne wbudowane drobiazgi, które pomogą ci określić, że twoje funkcje powinny generować ostrzeżenia oparte na posiadaniu wielu argumentów.Dokumentacja
Dodaj formularz kompilatora dla FUNKCJI. Jeśli funkcja jest symbolem, to zmienna „byte-SYMBOL” musi określać używany kod operacji. Jeśli funkcja jest listą, pierwszy element jest funkcją, a drugi element jest symbolem kodu bajtowego. Drugi element może być zerowy, co oznacza, że nie ma kodu operacji. COMPILE-HANDLER to funkcja używana do kompilacji tego bajtu-operacji, lub mogą to być skróty 0, 1, 2, 3, 0-1 lub 1-2. Jeśli jest zero, to program obsługi to „bajt-kompilacja-SYMBOL.
Stosowanie
W twoim konkretnym przypadku możesz użyć jednego ze skrótów, aby zdefiniować, że twoja funkcja powinna mieć dwa argumenty.
Teraz Twoja funkcja wyświetli ostrzeżenia, gdy zostanie skompilowana z czymkolwiek innym niż 2 argumenty.
Jeśli chcesz podać bardziej szczegółowe ostrzeżenia i napisać własne funkcje kompilatora. Sprawdź
byte-compile-one-arg
i inne podobne funkcje w bytecomp.el w celach informacyjnych.Zauważ, że nie tylko określasz jakąś funkcję do obsługi sprawdzania poprawności, ale tak naprawdę kompilację. Ponownie funkcje kompilacji w bytecomp.el zapewnią ci dobre referencje.
Bezpieczniejsze trasy
Nie jest to coś, co widziałem udokumentowane lub omówione online, ale ogólnie powiedziałbym, że jest to odradzana droga do wyboru. Prawidłowa trasa (IMO) polegałaby na napisaniu defuntu za pomocą podpisów opisowych lub użyciu makra, które zapewni ostrzeżenia w czasie ekspansji, sprawdzania długości argumentów i używania
byte-compile-warn
luberror
pokazywania błędów. Korzystne może być również skorzystanie z funkcjieval-when-compile
sprawdzania błędów.Będziesz także musiał zdefiniować swoją funkcję, zanim zostanie ona kiedykolwiek użyta, a wywołanie to
byte-defop-compiler
będzie musiało nastąpić, zanim kompilator przejdzie do rzeczywistych wywołań twojej funkcji.Ponownie wydaje się, że tak naprawdę nie zostało to udokumentowane ani wskazane na podstawie tego, co widziałem (może się mylić), ale wyobrażam sobie, że wzorzec, który należy zastosować tutaj, to określenie pewnego rodzaju pliku nagłówkowego dla twojego pakietu, który jest pełen wielu pustych odrzuceń i wzywa do
byte-defop-compiler
. Zasadniczo byłby to pakiet wymagany przed skompilowaniem prawdziwego pakietu.Opinia: Na podstawie tego, co wiem, co nie jest wiele, ponieważ właśnie się o tym wszystkim dowiedziałem, radziłbym wam nigdy tego nie robić. zawsze
źródło