2009-02-23 14 views
18

Próbuję nauczyć się ostatnio Lisp (Common Lisp) i zastanawiam się, czy istnieje sposób na podawanie liczb stałych tak, jak można to zrobić w C poprzez wyliczenia.Common Lisp odpowiednik C enum

Nie potrzebuję pełnego zestawu funkcji wyliczeniowych. W końcu chcę mieć szybki czytelny kod i.

Próbowałem już globałów i niewielkich funkcji, ale zawsze było to związane z pogorszeniem wydajności. Po prostu podłączenie liczb do kodu było zawsze szybsze.

Odpowiedz

22

normalny sposób zrobić wyliczeń w Lisp jest użycie symboli . Symbole zostają internowane (zastąpione wskaźnikami do ich pozycji w tabeli symboli), więc są tak szybkie, jak liczby całkowite i czytelne jako stałe wyliczone w innych językach.

Więc gdzie w C można napisać:

 
enum { 
    apple, 
    orange, 
    banana, 
}; 

W Lisp można po prostu użyć 'apple i 'banana, 'orange bezpośrednio.

Jeśli potrzebujesz wyliczone typ, można określić jedną z deftype:

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

a następnie można użyć typu fruit w declare, typep, typecase i tak dalej, i można pisz standardowe funkcje, które specjalizują się w tym typie:.

+4

Tak, ale lepiej użyj słów kluczowych. – kmkaplan

+0

To dobra uwaga: dzięki słowu kluczowemu unikasz martwienia się o to, w którym pakiecie znajdują się twoje symbole. –

+0

W rzeczywistości funkcje ogólne mogą specjalizować się tylko w klasach, a nie arbitralnie. –

6

wyliczenia są zbędne dla Lisp, Wynika to z faktu, że wszystkie symbole są własnej tożsamości, więc może po prostu użyć tych, na przykład:

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

Na przykład chcesz wymienić rozmiary czcionek:

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

Mogłeś napisać makro, aby to krótsze.

Powyższe definicje stałe są zazwyczaj TYLKO napisane, gdy liczby te należy przekazać do zewnętrznego kodu innego niż Lisp.

W przeciwnym razie wystarczy użyć symboli słów kluczowych:: duży,: średni i: mały.

Możesz je przetestować za pomocą EQ i wszystkiego, co używa jakiegoś testu dla równości.

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

Można również napisać metod IT:

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

Jak wspomniano można zdefiniować typ zestawie:

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

Następnie można sprawdzić, czy coś jest jedną z tych:

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

Powyżej zasygnalizować błąd, jeśli mój rozmiar nie jest jednym z: small,: medium or: large.

Można również użyć typu w formie defclass:

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

Teraz można utworzyć obiektów jak tutaj:

(make-instance 'vehicle :width :large) 

Niektóre wspólne implementacje Lisp sprawdzi kiedy ustawić gniazdo do niektórych nielegalna wartość.

Jeśli teraz tworzysz obiekty klasy pojazdu, gniazda będą następujące: duży,: średni lub: mały. Jeśli spojrzysz na obiekt w debugerze, inspektorze lub jakimś innym narzędziu, zobaczysz nazwy symboliczne, a nie 1, 2 lub 3 (lub jakiekolwiek wartości, których normalnie używasz).

Jest to część stylu Lisp: w miarę możliwości używaj nazw symbolicznych. Używaj symboli z wartościami liczbowymi tylko w kodzie interfejsu dla obcych funkcji (np. Wywoływanie zewnętrznego kodu C, który używa enums).

+1

Stałe liczbowe (w przeciwieństwie do słów kluczowych) mogą być również przydatne, jeśli chcesz zachować Wielkie tablice rzeczy, jak w światowym modelu gry lub symulacji. Twoja "mapa obłożenia" mogłaby zatem kompaktowo i skutecznie śledzić różne rodzaje rzeczy, takie jak mury i potwory oraz skarby. Interwały stałych mogą być używane do klasyfikowania elementów, jak w '(defun monster-p (x) (i (> = x + najmniejszy-potwór-numer +) (<= x + największy-potwór-numer +)))' –

+0

powyższe wywołania "defconstant", w połączeniu z późniejszymi przykładami użycia słów kluczowych, wydaje mi się, że jeden z nich może mieszać te dwa mają coś podobnego do funkcji, gdy przechodzi się do kodu nie będącego seplenieniem: '(defun size-number (thesize) (rozmiar typu check-size) (case (: small + small +) (: medium + medium +) (: large + large +))) '. A może pominąć 'check-type' i używa' ecase' zamiast 'case'. Odwrotna "liczba do wielkości" może być podobnie napisana. – lindes