2013-07-09 7 views
5

Sto provando a definire una struttura con alcune proprietà che conosco e un numero arbitrario di altre proprietà che non sono necessarie per la struttura di base.Come definire una struttura in lisp con un numero arbitrario di argomenti?

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

so in una funzione che si può fare:

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

C'è una sorta di &rest equivalente per defstruct?

Sto solo imparando la focalizzazione, quindi ho la sensazione che mi manchi qualcosa. Se non c'è un equivalente &rest, qualche idea su come potrei andare su questo? Grazie in anticipo!

risposta

6

Non è chiaro esattamente quello che stai cercando. Il caso predefinito per le strutture è un tipo di record con un numero fisso di slot, ognuno dei quali ha un nome ed è accessibile tramite una funzione generata dalla macro defstruct. Per esempio, una volta che hai fatto

(defstruct node 
    label) 

è possibile accedere un'etichetta node s' con node-label e ottenere il tempo di ricerca veloce (dal momento che è in genere solo un indice in un pezzo di memoria). Ora, come stai facendo, puoi scegliere di utilizzare gli elenchi come implementazione delle strutture, nel qual caso node-label è solo un alias per car o 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)) 

Se siete alla ricerca di arbitrarie coppie di valori chiave basati su elenco, probabilmente desidera un property list, per il quale Common Lisp contiene alcune funzioni di convenienza.

Se si desidera avere una struttura che contiene anche un elenco di proprietà, è possibile aggiungere un costruttore speciale compilare tale elenco. Ad esempio,

(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

Grazie, questo è quasi esattamente quello che sto cercando. Ma c'è un modo per avere l'elenco delle proprietà, come lo metti, come solo altri slot nel 'nodo'? In altre parole, c'è un modo per fare '(make-node 'some-label: one 1: two 2) => (SOME-LABEL: ONE 1: TWO 2)', invece di '(SOME-LABEL (: ONE 1: TWO 2)) '? –

+1

In breve, no. [La risposta di Rainer] (http://stackoverflow.com/a/17556349/1281433) approfondisce le strutture in generale, ma il punto è che le strutture hanno un numero fisso di slot. Anche se puoi usare 'defstruct' per creare" record di poveri "con l'opzione' (: type list) ', hai ancora solo un numero fisso di slot (il che significa che l'elenco ha un numero fisso di elementi). Sembra davvero che tu voglia solo una lista di proprietà. –

+0

Sì, leggendo la risposta di Rainer, penso che ci sia più potenza di fuoco di quanto ho bisogno per quello che sto facendo. Grazie per il consiglio, ragazzi! –

7

In Common Lisp, le strutture sono considerate record rigidi e di basso livello. Non hanno caratteristiche dinamiche fantasiose.

Quello che si può fare con le strutture è definire un nuovo tipo di struttura che eredita da un altro. È disponibile un'eredità singola.

Per gestire l'estensibilità dinamica, un modo tipico è aggiungere uno slot di elenco di proprietà a una struttura. Vedi la risposta di Giosuè.

Quindi esiste il Common Lisp Object System, che fornisce ereditarietà multipla e che è possibile modificare le classi in fase di runtime. Quindi puoi aggiungere uno slot ad una classe e le istanze di quell'aggiornamento di classe. Puoi anche modificare la classe di un oggetto e gli slot possono essere aggiunti o eliminati. Tuttavia, in genere tutte le istanze di una classe avranno lo stesso set di slot. Di nuovo, si vede che uno slot con un elenco di proprietà può essere aggiunto e utilizzato per l'estensibilità.

Esistono altri sistemi di oggetti per Common Lisp, che possono facilmente aggiungere slot su una base peristanza. Ma di solito è troppo per usarli solo per quello, dal momento che sono un po 'più potenti.

Con CLOS e il protocollo Meta-oggetto si può provare a nasconderlo.Qui sto usando LispWorks:

Definiamo una classe intermedia per le nostre proprietà:

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

impostazione e di lettura delle proprietà:

(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)) 

Ora dobbiamo scrivere i metodi per rendere SLOT-VALUE accettare i nostri nomi di proprietà :

(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))) 

Esempio. Si definisce una classe di automobili con due slot:

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

ora un'istanza:

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

Siamo in grado di ottenere un normale valore di slot:

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

Scriviamo ad uno slot che non lo fa esiste, ma sarà aggiunto alla nostra lista di proprietà:

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

Possiamo ottenere il valore indietro:

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

ho pensato che questo sarebbe merita una risposta separata piuttosto che un commento, quindi ecco qui:

Alcune volte, quando pensi di aver bisogno di una struttura o di un oggetto, ma hai alcuni requisiti speciali che queste entità non soddisfano, forse perché ciò di cui hai effettivamente bisogno è una struttura di dati diversa? Gli oggetti o le strutture sono utili quando alcune condizioni sono soddisfatte, una di queste condizioni è che gli slot sono noti staticamente - ciò consente al compilatore di ragionare meglio sul codice, che è sia utile per l'ottimizzazione che per la segnalazione degli errori.

D'altra parte, ci sono strutture dati. Alcuni forniti con la libreria standard della lingua, altri aggiunti su di esso. Ecco una libreria che fornisce molti di loro: http://cliki.net/cl-containers ma ce ne sono ancora di più per casi speciali.

Ora, sosterrò che l'uso di una struttura come lista, array, una sorta di albero ecc. È meglio quindi provare ad estendere oggetti o strutture per consentire l'aggiunta di slot in modo dinamico. Questo perché di solito ci aspettiamo che il tempo per accedere ad uno slot sia trascurabile. Questo è che ci aspettiamo che sia O (1). Questo è ciò che normalmente accade indipendentemente dal numero di slot di un oggetto. Ora, quando stai usando una lista sotto la stai facendo O (n), mentre mantieni la stessa semantica! Potresti, ovviamente, usare un hash-table per renderlo O (1) (anche se questo sarà ancora più lento di accesso allo slot), ma poi avrai qualche altro comportamento inaspettato, come ad esempio nil restituito quando lo slot non esiste invece l'errore normale ecc.

Non penso che estendere gli oggetti in questo modo sia una pratica comune in CL, questo è probabilmente il motivo per cui altre risposte non ti scoraggiano dal farlo. Conosco meno CL di altri intervistati, ma ho avuto un sacco di dolore con questo tipo di manipolazioni in un'altra lingua, dove questo è comune e solitamente scoraggiato.