2013-07-05 6 views
2

Я пишу программу Lisp и стараюсь быть немного добросовестным в отношении типов. Я думаю, что есть улучшения производительности, но меня больше интересует использование аннотаций типа для документации и безопасности. Проблема nil. До сих пор я столкнулся с двумя проблемами.Обращайтесь с номером типа wildcard

Приложение A:

>(defmethod foo ((bar bar-class) (quux quux-class)) 
    ...) 

>(foo (make-instance 'bar-class) nil) 
ERROR: No applicable method, etcetera etcetera, because nil is not of type quux-class 

Приложение B:

(defmethod initialize-instance :after ((f foo) &rest args) 
    "Initialize the grid to be the right size, based on the height and width of the foo." 
    (declare (ignorable args)) 
    (setf (slot-value f 'grid) (make-array (list (width f) (height f)) 
             :element-type 'foo-component 
             :adjustable nil 
             :initial-element nil))) 

style-warning: 
    NIL is not a FOO-COMPONENT. 

Что лучшая практика здесь? До сих пор единственная проницательная идея, которую я имел, заключается в использовании null object pattern и имеет (defclass nil-quux-class (quux-class) ...) и (defclass nil-foo-component (foo-component) ...), но это кажется в лучшем случае взломанным. Я не уверен почему, но он делает. Откровенно говоря, я не привык, чтобы проектировать patterny обходные в CLOS :)

ответ

5

(A) Что вы хотите, чтобы произошло, когда вы звоните foo с nil для quux аргумента?

Если вы не хотите вообще ничего не произойдет, то

(defmethod foo ((bar bar-class) (quux null)) 
    nil) 

сортирует вас.

Если вы хотите, один и тот же код, который будет называться, если бы вы сдали экземпляр quux-class, а затем либо:

(defmethod foo ((bar bar-class) (quux quux-class)) 
    (do-stuff bar quux)) 

(defmethod foo ((bar bar-class) (quux null)) 
    (do-stuff bar quux)) 

или:

(defmethod foo ((bar bar-class) quux) 
    (unless (or (typep bar 'bar-class) 
       (null bar)) 
    (error "Expected a bar-class or null, got ~s." quux)) 
    (do-stuff bar quux)) 

(B) вы прошли

(make-array size :element-type 'foo-component 
       :initial-element nil) 

и ваша реализация lisp указывает на противоречие - начальные элементы не могут быть как nil, так и foo-component s. (Ну, я думаю, это зависит от того, что ваш тип foo-component выглядит как я предполагаю, он не включает в себя null.).

Вы могли бы рассмотреть:

(make-array :element-type '(or foo-component null) 
      :initial-element nil) 

, но быть в курсе: что делать вы хотите, чтобы ваш lisp получил от знания, что массив будет содержать либо foo-component s, либо nil s? Оптимизация? Ошибка от вашего имени? (Ваш пробег может различаться, в соответствии с которым вы используете lisp.)

+0

Спасибо! A) Я не был в восторге от дублирования кода (дважды звоня по телефону), но, на мой взгляд, это имеет смысл. И по мере того, как программа растет, я, возможно, очень хочу обрабатывать 'nil' по-разному, что это позволит мне сделать. B) Да, мне нужна проверка ошибок. SBCL очень хорош в этом. – tsm

+0

ОК. Btw Я добавил редактирование для вашего вызова для создания массива (фиксированные парсы и отступы); Наверное, это со временем появится ... –

4

Обратите внимание, что тип элемента в MAKE-ARRAY не является декларацией реального типа. Это намек на реализацию Lisp, какие данные должен хранить массив. Затем он может выбирать специализированные реализации массива или нет.

UPGRADED-ARRAY-ELEMENT-TYPE возвращает тип элемента наиболее специализированного представления массива, способного удерживать элементы типа, обозначенные typespec.

CL-USER 12 > (upgraded-array-element-type '(integer 0 100)) 
(UNSIGNED-BYTE 8) 

выше означает, что я прошу массив с целыми элементами в диапазоне от 0 до 100. Этот Lisp (здесь LispWorks) даст мне массив элементов типа (unsigned-byte 8).

Другие примеры:

CL-USER 13 > (upgraded-array-element-type 'fixnum) 
(SIGNED-BYTE 64) 

CL-USER 14 > (upgraded-array-element-type 'complex) 
T 

CL-USER 15 > (defclass foo-component()()) 
#<STANDARD-CLASS FOO-COMPONENT 402030196B> 

CL-USER 16 > (upgraded-array-element-type 'foo-component) 
T 

T здесь означает, что массив на самом деле будет хранить все виды объектов данных.

CL-USER 17 > (upgraded-array-element-type '(or null foo-component)) 
T 

CL-USER 20 > (make-array 2 
         :element-type 'foo-component 
         :initial-element (make-instance 'foo-component)) 
#(#<FOO-COMPONENT 40203B9A03> #<FOO-COMPONENT 40203B9A03>) 

CL-USER 21 > (array-element-type *) 
T 

Выше показано, что Lisp также забывает, что было предложено изначально. На самом деле мы получили массив элементарного типа T, и когда мы запрашиваем его тип элемента, то это T.

 Смежные вопросы

  • Нет связанных вопросов^_^