Подтвердить что ты не робот

Общий Lisp эквивалент C перечислений

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

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

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

4b9b3361

Ответ 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]>