2009-02-23 14 views
18

Ultimamente sto cercando di imparare un po 'di Lisp (Common Lisp), e mi chiedo se c'è un modo per dare un numero costante ai nomi proprio come si può fare in C tramite enumerazioni.Common Lisp equivalente a C enums

Non ho bisogno dell'intero set di enumerazione. Alla fine voglio solo avere il codice leggibile e leggibile.

Ho provato i globali e le piccole funzioni, ma questo ha sempre comportato un peggioramento delle prestazioni. Il semplice inserimento dei numeri nel codice era sempre più veloce.

risposta

22

Il modo normale di fare le enumerazioni in Lisp è quello di utilizzare i simboli . I simboli vengono internati (sostituiti con puntatori alle loro voci in una tabella dei simboli) in modo che siano veloci come interi e leggibili come costanti numerate in altre lingue.

Allora, dove in C si potrebbe scrivere:

 
enum { 
    apple, 
    orange, 
    banana, 
}; 

In Lisp si può semplicemente utilizzare 'apple, 'orange e 'banana direttamente.

Se avete bisogno di un enumerato tipo, allora si può definire uno con deftype:

(deftype fruit() '(member apple orange banana))

e quindi è possibile utilizzare il tipo di fruit in declare, typep, typecase e così via, e si può scrivere funzioni generiche specializzate su quel tipo.

+4

Sì, ma utilizzare le parole chiave meglio. – kmkaplan

+0

Questo è un buon punto: utilizzando la parola chiave si evita di doversi preoccupare del pacchetto in cui si trovano i simboli. –

+0

In realtà, le funzioni generiche possono specializzarsi solo sulle classi, non sui tipi arbitrari. –

6

enumerazioni sono ridondanti per Lisp, il motivo è che tutti i simboli sono la loro propria identità, così puoi semplicemente usare quelli, ad esempio:

[[email protected]:~]$ clisp -q 
[1]> (setf x 'some) ;' 
SOME 
[2]> (eq x 'some) ;' 
T 
[3]> 
15

Per esempio si desidera denominare le dimensioni dei caratteri:

(defconstant +large+ 3) 
(defconstant +medium+ 2) 
(defconstant +small+ 1) 

Si potrebbe scrivere una macro per fare quel breve.

Sopra le definizioni costanti di solito vengono scritte SOLO quando questi numeri devono essere passati a un codice esterno non Lisp.

Altrimenti si userebbero solo simboli di parole chiave:: grande,: medio e: piccolo.

È possibile testarli con EQ e tutto ciò che utilizza un test per l'uguaglianza.

(let ((size :medium)) 
    (ecase size 
    (:small ...) 
    (:medium ...) 
    (:large ...))) 

Si può anche scrivere i metodi per esso:

(defmethod draw-string (message x y (size (eql :large))) ...) 

Come accennato si potrebbe definire un tipo di set:

(deftype size() '(member :small :medium :large)) 

Quindi è possibile controllare se qualcosa è uno di questi:

(let ((my-size :medium)) 
    (check-type my-size size)) 

Sopra sarebbe segnala un errore se my-size non è uno dei seguenti: small,: medium o: large.

È inoltre possibile utilizzare il tipo in una forma defclass:

(defclass vehicle() 
    ((width :type size :initarg :width))) 

Ora si potrebbe creare oggetti come qui:

(make-instance 'vehicle :width :large) 

Alcune implementazioni Common Lisp controllerà quando si imposta lo slot a qualche valore illegale

Se ora create oggetti di classe veicolo, gli slot saranno uno di: grande,: medio o: piccolo. Se guardi l'oggetto in un debugger, in un ispettore o in qualche altro strumento, vedrai i nomi simbolici e non 1, 2 o 3 (o qualunque valore tu usi normalmente).

Questo è parte dello stile Lisp: utilizzare nomi simbolici quando possibile. Utilizzare i simboli con valori numerici solo nel codice di interfaccia per le funzioni esterne (ad esempio chiamando un codice C esterno che utilizza enumerazioni).

+1

Le costanti numeriche (al contrario delle parole chiave) potrebbero anche essere utili se vuoi mantenere matrici grosse di cose, come in un modello mondiale per un gioco o una simulazione. La tua "mappa di occupazione", quindi, potrebbe tracciare in modo compatto ed efficiente diversi tipi di cose come muri e mostri e tesori. Gli intervalli di costanti possono essere usati per classificare le entità, come in '(defun monster-p (x) (e (> = x + least-monster-number +) (<= x + greatest-mostro-numero +)))' –

+0

Dato le chiamate 'defconstant' di cui sopra, mescolate con gli esempi successivi di usare le parole chiave, mi sembra che si possa mescolare questi due hanno qualcosa come il seguente come una funzione per quando si passa al codice non-lisp:' (defun size-number (thesize) (check-type size size) (case (: small + small +) (: medium + medium +) (: large + large +))) '. O forse si salta il 'check-type' e si usa' ecase' invece di 'case'. Potrebbe essere scritto in modo analogo un 'numero a misura reciproco '. – lindes