Общий Lisp эквивалент C перечислений
В последнее время я пытаюсь изучить некоторые Lisp (Common Lisp), и мне интересно, есть ли способ дать постоянным номерам имя так же, как вы можете сделать в C через enums.
Мне не нужен полный набор перечислений. В конце концов, я просто хочу иметь быстрый и понятный код.
Я пробовал глобалы и небольшие функции, но это всегда сопровождалось деградацией производительности. Простое включение цифр в код всегда было быстрее.
Ответы
Ответ 1
Нормальным способом выполнения перечислений в Lisp является использование символов. Символы становятся интернированными (заменяются указателями на их записи в таблице символов), поэтому они так же быстро, как целые числа и считаются читаемыми как перечисленные константы на других языках.
Итак, где в C вы можете написать:
enum {
apple,
orange,
banana,
};
В Lisp вы можете просто использовать 'apple
, 'orange
и 'banana
.
Если вам нужен нумерованный тип, вы можете определить его с помощью deftype
:
(deftype fruit () '(member apple orange banana))
а затем вы можете использовать тип fruit
в declare
, typep
, typecase
и т.д., и вы можете писать общие функции, которые специализируются на этом типе.
Ответ 2
Например, вы хотите назвать размер шрифта:
(defconstant +large+ 3)
(defconstant +medium+ 2)
(defconstant +small+ 1)
Вы можете написать макрос, чтобы сделать это короче.
Выше константных определений обычно записывается ТОЛЬКО, когда эти числа должны быть переданы на какой-то внешний код не Lisp.
В противном случае можно просто использовать символы ключевых слов:: 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))
Выше будет сигнализировать об ошибке, если мой размер не является одним из: small,: medium или: large.
Вы также можете использовать тип в форме defclass:
(defclass vehicle ()
((width :type size :initarg :width)))
Теперь вы создадите объекты, как здесь:
(make-instance 'vehicle :width :large)
Некоторые общие реализации Lisp будут проверять, когда вы установите для этого слота какое-то незаконное значение.
Если вы теперь создадите объекты класса транспортного средства, слоты будут одно из: большого, среднего или малого. Если вы посмотрите на объект в отладчике, инспекторе или другом инструменте, вы увидите символические имена, а не 1, 2 или 3 (или любые значения, которые вы обычно использовали).
Это часть стиля Lisp: используйте по возможности символические имена. Используйте символы с числовыми значениями только в коде интерфейса для внешних функций (например, вызывать внешний код C, который использует перечисления).
Ответ 3
Перечисления избыточны для Lisp, потому что все символы являются их собственной идентичностью, поэтому вы можете просто использовать их, например:
[[email protected]:~]$ clisp -q
[1]> (setf x 'some) ;'
SOME
[2]> (eq x 'some) ;'
T
[3]>