Common Lisp, эквивалентный C перечислениям - PullRequest
18 голосов
/ 23 февраля 2009

В последнее время я пытаюсь выучить какой-нибудь Лисп (Common Lisp), и мне интересно, есть ли способ дать константным числам имя, такое же, как вы можете сделать это в C с помощью перечислений.

Мне не нужен полный набор функций перечислений. В конце я просто хочу иметь быстрый и читаемый код.

Я пробовал глобальные переменные и небольшие функции, но это всегда приводило к снижению производительности. Просто вставлять цифры в код всегда было быстрее.

Ответы [ 3 ]

21 голосов
/ 23 февраля 2009

Обычный способ сделать перечисления в Лиспе - это использовать символы. Символы интернируются (заменяются указателями на их записи в таблице символов), поэтому они так же быстры, как целые числа, и читаемы, как перечисляемые константы в других языках.

Итак, где в C вы можете написать:

enum {
   apple,
   orange,
   banana,
};

В Лиспе вы можете просто использовать 'apple, 'orange и 'banana напрямую.

Если вам нужен перечислимый тип , то вы можете определить его с помощью deftype:

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

и затем вы можете использовать тип fruit в declare, typep, typecase и т. Д. , и вы можете написать универсальные функции, специализирующиеся на этом типе .

15 голосов
/ 25 февраля 2009

Например, вы хотите назвать размеры шрифта:

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

Вы можете написать макрос, чтобы сделать это короче.

Определения выше констант обычно пишутся ТОЛЬКО тогда, когда эти числа должны быть переданы какому-либо внешнему не Лисп-коду.

В противном случае можно было бы просто использовать ключевые слова:: large,: medium и: small.

Вы можете проверить их с помощью EQ и всего, что использует какой-то тест на равенство.

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

Вы также можете написать методы для этого:

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

Как уже упоминалось, вы можете определить тип набора:

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

Тогда вы можете проверить, является ли что-то из этого:

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

Выше будет сигнализировать об ошибке, если my-size не является одним из: small,: medium или: large.

Вы также можете использовать тип в форме defclass:

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

Теперь вы будете создавать объекты, как здесь:

(make-instance 'vehicle :width :large)

Некоторые реализации Common Lisp проверят, когда вы установите для слота недопустимое значение.

Если вы сейчас создаете объекты класса транспортного средства, слоты будут одним из: больших, средних или маленьких. Если вы посмотрите на объект в отладчике, инспекторе или другом инструменте, вы увидите символические имена, а не 1, 2 или 3 (или любые другие значения, которые вы обычно используете).

Это часть стиля Lisp: используйте символические имена, когда это возможно. Используйте символы с числовыми значениями только в коде интерфейса для внешних функций (например, для вызова внешнего кода C, использующего перечисления).

6 голосов
/ 23 февраля 2009

Перечисления избыточны для Lisp, причина в том, что все символы являются их собственной идентификацией, поэтому вы можете просто использовать их, например:

[dsm@localhost:~]$ clisp -q
[1]> (setf x 'some) ;'
SOME
[2]> (eq x 'some) ;'
T
[3]>
...