2013-07-09 18 views
5

Próbuję zdefiniować strukturę z pewnymi właściwościami, których potrzebuję, i dowolną liczbą innych właściwości, które nie są niezbędne dla podstawowej struktury.Jak zdefiniować strukturę w seplenie z dowolną liczbą argumentów?

(defstruct (node (:type list)) label [other args here]) 

wiem w funkcji można zrobić:

(defun foo (arg1 &rest args) ...) 

Czy istnieje jakiś &rest odpowiednik dla defstruct?

Po prostu uczę się seplenienia, więc mam wrażenie, że czegoś mi brakuje. Jeśli nie ma odpowiednika w wersji &rest, masz pomysły na to, jak to zrobić? Z góry dziękuję!

Odpowiedz

6

Nie jest jasne, czego dokładnie szukasz. Domyślnym przypadkiem dla struktur jest typ rekordu ze stałą liczbą slotów, z których każdy ma nazwę i jest dostępny za pośrednictwem funkcji wygenerowanej przez makro defstruct. Na przykład raz zrobiłeś

(defstruct node 
    label) 

można uzyskać dostęp etykietę node „sz node-label i uzyskać szybki czas odnośnika (ponieważ jest to zazwyczaj tylko indeks na fragmencie pamięci). Teraz, gdy robisz, możesz zdecydować się na używanie list jako implementacji struktur, w którym to przypadku node-label to tylko alias dla car lub first.

(defstruct (node (:type list)) 
    label) 

CL-USER> (make-node :label 'some-label) 
(SOME-LABEL) 
CL-USER> (node-label (make-node :label 'some-label)) 
SOME-LABEL 
CL-USER> (first (make-node :label 'some-label)) 
SOME-LABEL 
CL-USER> (car (make-node :label 'some-label)) 

Jeśli szukasz arbitralnych lista oparte par wartości klucza, to prawdopodobnie chcą property list, dla których Common Lisp zawiera kilka funkcji wygody.

Jeśli chcesz mieć strukturę, która również zawiera listę właściwości, możesz dodać specjalny konstruktor, który ją zapełni. Na przykład:

(defstruct (node (:type list) 
       (:constructor make-node (label &rest plist))) 
    label 
    plist) 

CL-USER> (make-node 'some-label :one 1 :two 2) 
(SOME-LABEL (:ONE 1 :TWO 2)) 
CL-USER> (node-plist (make-node 'some-label :one 1 :two 2)) 
(:ONE 1 :TWO 2) 
+0

Dzięki, prawie dokładnie to, czego szukam. Ale czy istnieje jakiś sposób, aby mieć listę właściwości, jak to ujęłeś, jako tylko inne gniazda w 'węźle'? Innymi słowy, czy istnieje sposób zrobienia '(make-node 'some-label: one 1: two 2) => (SOME-LABEL: ONE 1: TWO 2)', zamiast '(SOME-LABEL (: ONE 1: TWO 2)) '? –

+1

W skrócie, nie. [Odpowiedź Rainera] (http://stackoverflow.com/a/17556349/1281433) idzie głębiej o struktury w ogóle, ale chodzi o to, że struktury mają ustaloną liczbę slotów. Nawet jeśli możesz użyć 'defstruct' do utworzenia" rekordów biednych ludzi "za pomocą opcji' (: type list) ', nadal będziesz mieć stałą liczbę slotów (co oznacza, że ​​lista ma ustaloną liczbę elementy). To naprawdę brzmi, jakbyś chciał tylko listę nieruchomości. –

+0

Tak, czytając odpowiedź Rainera, myślę, że to jest więcej siły ognia, niż potrzebuję do tego, co robię. Dzięki za radę, chłopaki! –

7

W Common Lisp struktury są traktowane jako zapisy sztywne i niskiego poziomu. Nie mają fantazyjnych dynamicznych funkcji.

To, co można zrobić ze strukturami, to określenie nowego typu struktury, która dziedziczy po innej. Dostępne jest pojedyncze dziedziczenie.

Aby poradzić sobie z dynamiczną rozszerzalnością, typowym sposobem jest dodanie szczeliny listy właściwości do struktury. Zobacz odpowiedź Jozuego.

Następnie istnieje system Common Lisp Object, który zapewnia wielokrotne dziedziczenie i można zmieniać klasy w czasie wykonywania. Możesz więc dodać slot do klasy i same wystąpienia tej klasy. Możesz także zmienić klasę obiektu i gniazda mogą zostać dodane lub usunięte. Jednak zazwyczaj wszystkie wystąpienia klasy będą miały ten sam zestaw gniazd. Ponownie widzi się, że slot z listą właściwości można dodać i wykorzystać do rozszerzenia.

Istnieją inne systemy obiektowe dla Common Lisp, które mogą z łatwością dodawać gniazda w bazie dla każdej instancji. Ale zwykle jest ich za dużo, aby używać ich tylko do tego, ponieważ są one nieco mocniejsze.

Dzięki CLOS i protokołowi Meta-obiektu można spróbować go ukryć.Tutaj używam LispWorks:

Definiujemy wstawką klasę dla naszych nieruchomości:

(defclass property-mixin() 
    ((plist :initform nil)) 
    #+lispworks 
    (:optimize-slot-access nil)) 

ustawienie i odczyt właściwości:

(defmethod set-property ((object property-mixin) key value) 
    (setf (getf (slot-value object 'plist) key) value)) 

(defmethod get-property ((object property-mixin) key) 
    (getf (slot-value object 'plist) key)) 

Teraz piszemy metod, aby SLOT-VALUE akceptując nasze imiona własności :

(defmethod (setf clos:slot-value-using-class) 
     (value (class standard-class) (object property-mixin) slot-name) 
    (declare (ignorable class)) 
    (if (slot-exists-p object slot-name) 
     (call-next-method) 
    (progn 
     (set-property object slot-name value) 
     value))) 

(defmethod clos:slot-value-using-class ((class standard-class) 
             (object property-mixin) 
             slot-name) 
    (declare (ignorable class)) 
    (if (slot-exists-p object slot-name) 
     (call-next-method) 
    (get-property object slot-name))) 

Przykład. Definiujemy klasę samochodowy z dwoma gniazdami:

(defclass automobile (property-mixin) 
    ((company :initarg :company) 
    (motor :initarg :motor)) 
    #+lispworks 
    (:optimize-slot-access nil)) 

obecnie przykład:

CL-USER 45 > (setf a6 (make-instance 'automobile :company :audi :motor :v6)) 
#<AUTOMOBILE 402005B47B> 

Możemy dostać normalną wartość gniazd:

CL-USER 46 > (slot-value c1 'motor) 
:V6 

Napiszmy do gniazda, które nie istnieje, ale zostanie dodany do naszej listy nieruchomości:

CL-USER 47 > (setf (slot-value a6 'seats) 4) 
4 

możemy uzyskać wartość z powrotem:

CL-USER 48 > (slot-value c1 'seats) 
4 
1

myślałem, że to warto byłoby oddzielny odpowiedź, a następnie komentarz, więc tu idzie:

Kilka razy, gdy uważasz, że potrzebujesz strukturę lub grupę obiekt, ale masz pewne specjalne wymagania, których te jednostki nie spełniają, być może dlatego, że potrzebna jest trochę inna struktura danych? Obiekty lub struktury są dobre, gdy spełnione są pewne warunki, jednym z takich warunków jest to, że szczeliny są statycznie znane - to pozwala kompilatorowi lepiej uzasadnić kod, co jest dobre zarówno dla optymalizacji, jak i zgłaszania błędów.

Z drugiej strony istnieją struktury danych. Niektóre są dostarczane z biblioteką języków standardowych, inne są dodawane do niej. Oto jedna biblioteka, która zapewnia wiele z nich: http://cliki.net/cl-containers, ale jest jeszcze więcej na specjalne przypadki.

Teraz będę argumentował, że użycie struktury takiej jak lista, tablica, jakieś drzewo itp. Jest lepsze niż próba rozszerzenia obiektów lub struktur w celu umożliwienia dynamicznego dodawania slotów. Dzieje się tak dlatego, ponieważ zazwyczaj spodziewamy się, że czas dostępu do boksa będzie znikomy. Oczekujemy, że będzie to O (1). Zwykle dzieje się tak niezależnie od liczby slotów, które obiekt ma. Teraz, gdy używasz listy pod spodem, robisz to O (n), podczas gdy ty zachowujesz tę samą semantykę! Możesz oczywiście użyć tabeli mieszającej, aby zrobić O (1) (chociaż będzie to nadal ogólnie wolniej niż dostęp do slotów), ale wtedy będziesz mieć inne nieoczekiwane zachowanie, takie jak nil zwrócony, gdy slot nie będzie istnieje zamiast zwykłego błędu itp.

Nie sądzę, że rozszerzanie obiektów w taki sposób jest powszechną praktyką w CL, prawdopodobnie dlatego inne odpowiedzi nie zniechęcają cię do robienia tego. Znam mniej CL niż innych respondentów, ale miałem dużo żalu z tego rodzaju manipulacjami w innym języku, gdzie jest to powszechne i zwykle zniechęcane.

Powiązane problemy