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

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

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

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

Ответов (3)

Решение

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

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

enum {
   яблоко,
   апельсин,
   банан,
};

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

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

(deftype fruit () '(член яблоко апельсин банан))

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

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

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

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

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

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

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

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

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

(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 vehicle ()
   ((width :type size :initarg :width)))

Теперь вы должны создать такие объекты, как здесь:

(make-instance 'vehicle :width :large)

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

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

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