* Uwaga: pomimo częstego korzystania z StackOverflow przez długi czas, jest to pierwsze pytanie, które sam napisałem. Przepraszam, jeśli jest trochę gadatliwy. Konstruktywna krytyka doceniona.Makro, które definiuje funkcje, których nazwy są oparte na argumentach makra.
Kiedy definiuję strukturę w Common Lisp przy użyciu funkcji defstruct, automatycznie generowana jest funkcja predykatu, która sprawdza, czy jej argument jest typu zdefiniowanego przez defstruct. Np:
(defstruct book
title
author)
(let ((huck-finn (make-book :title "The Adventures of Huckleberry Finn" :author "Mark Twain")))
(book-p huck-finn))
=> True
Jednak przy określaniu klasy przy użyciu defclass takie funkcje są pozornie nie generowane domyślnie (czy jest jakiś sposób, aby to określić?), Więc staram się dodać tę funkcjonalność siebie, bo d) a) aby składnia ta była zgodna między strukturami i klasami, b) mieć skrót (typep obj 'classname)
, który muszę napisać bardzo często i jest hałaśliwy wizualnie, i c) jako ćwiczenie programistyczne, ponieważ jestem wciąż stosunkowo nowe w Lisp.
mógłbym napisać makro, które definiuje funkcję predykatu daną nazwą klasy:
(defclass book()
((title :initarg :title
:accessor title)
(author :initarg :author
:accessor author)))
;This...
(defmacro gen-predicate (classname)
...)
;...should expand to this...
(defun book-p (obj)
(typep obj 'book))
;...when called like this:
(gen-predicate 'book)
imię, które trzeba przekazać do defun musi mieć postać 'classname-p
. Tutaj mam trudności. Aby stworzyć taki symbol, mógłbym użyć funkcji "symb" z książki On Lisp Paula Grahama (s. Gdy jest prowadzony na REPL:
(symb 'book '-p)
=> BOOK-P
moich gen-źródłowych wygląd makro-, jak to do tej pory:
(defmacro gen-predicate (classname)
`(defun ,(symb classname '-p) (obj)
(typep obj ,classname)))
(macroexpand `(gen-predicate 'book))
=>
(PROGN
(EVAL-WHEN (:COMPILE-TOPLEVEL) (SB-C:%COMPILER-DEFUN '|'BOOK-P| 'NIL T))
(SB-IMPL::%DEFUN '|'BOOK-P|
(SB-INT:NAMED-LAMBDA |'BOOK-P|
(OBJ)
(BLOCK |'BOOK-P| (TYPEP OBJ 'BOOK)))
NIL 'NIL (SB-C:SOURCE-LOCATION)))
T
Wydawałoby się, że symbol stworzony przez (symb 'book '-p)
jest faktycznie uznane |'BOOK-P|
przez wdrożenie (SBCL), a nie BOOK-P
. Rzeczywiście, to teraz działa:
(let ((huck-finn (make-instance 'book)))
(|'BOOK-P| huck-finn))
=> True
Dlaczego symbol stworzony przez symb internowany jako |'BOOK-P|
? W On Lisp (ta sama strona co powyżej) Graham mówi: "Dowolny ciąg znaków może być nazwą-print symbolu, nawet łańcuchem zawierającym małe litery lub znaki makr takie jak nawiasy. Jeśli nazwa symbolu zawiera takie osobliwości, jest drukowana w pionie bary. " W tym przypadku nie ma takich dziwactw, prawda? Czy mam rację, myśląc, że "nazwa-wydruku" symbolu jest tym, co jest faktycznie wyświetlane na standardowym wyjściu, gdy drukowany jest symbol, i czy w przypadku takich osobliwości różni się od postaci samego symbolu?
Aby móc pisać makra definiujące funkcje, takie jak gen-predicate
- których zdefiniowane funkcje są nazwane na podstawie argumentów przekazanych do makra - wydaje mi się, że prawdopodobnie hackerzy Lisp prawdopodobnie robili od wieków. Użytkownik Kaz mówi tutaj (Merging symbols in common lisp), że często można uniknąć "zlepiania" symboli, ale to byłoby sprzeczne z celem tego makra.
Wreszcie, zakładając, że mogę uzyskać gen-predicate
, aby działał tak, jak chcę, jaki byłby najlepszy sposób zagwarantowania, że zostanie on nazwany dla każdej nowej klasy, ponieważ są one zdefiniowane? Podobnie jak initialize-instance
można dostosować w celu wykonywania określonych czynności na klasie, czy istnieje ogólna funkcja wywoływana przez klasę deflacji, która może wykonywać akcje na klasie klasy ?
Dziękuję.
Oczywiście, genialny! Jakoś nie uważałem, że '' książka' jest naprawdę '(quote book)'. Rozumiem twoje drugie rozwiązanie, ale pierwsze jest mylące. W istocie zdałem sobie już sprawę, że (macroexpand '(książka predykatów genów) rozwija się w to, co chciałem, ale ponieważ symbol' book' nie jest cytowany, to jest interpretowany jako zmienna, która jest niezdefiniowana. Aby uniknąć tego problemu, dziękuję za szybką i pomocną odpowiedź! –
Ta odpowiedź dobrze się sprawdza w pierwszej części pytania: jest też druga część, z której chciałbym się zapoznać: jak uzyskać to, aby zawsze było wywoływane, gdy tworzone są nowe klasy? (I/lub kiedy chciałoby się lub nie chciałoby się tego dokonać?) – lindes