О параметрах вызова и “перегрузке” lisp-функций
Известно, что в “нормальных” языках программирования (таких, как Visual Basic, C#, C++ и т.п.) есть возможность создавать так называемые перегруженные функции, или функции с разным количеством и / или типом параметров. В AutoLISP / VisualLISP подобной возможности не предусмотрено. То есть если пишется функция создания, например, отрезка, то ей должны передаваться сразу все параметры: начальная и конечная точки, пространство-владелец создаваемого объекта (точнее, указатель на него), тип линии, цвет, и еще безумное количество устанавливаемых свойств. Запомнить их последовательность нереально, особенно учитывая тот факт, что в VLIDE напрочь отсутствует технология IntelliSence. Но не все так плохо. Обойти это ограничение можно и нужно. Об этом и поговорим.
Разве кто-то мешает нам воспользоваться тем, что LISP ориентирован на обработку списков? По-моему, никто. А раз так, то и будем передавать в ту же функцию пару-тройку параметров, один из которых – список, сформированный по заранее определенным правилам.
Рассмотрим пример создания отрезка. Нам гарантированно надо передавать указатель на пространство-владелец создаваемого объекта; координаты начальной точки; координаты конечной точки; и… И все, остальные свойства должны приниматься по какому-то правилу (к примеру: если не указывается, то слой принимать текущим; если не указывается, то тип линии – текущий и так далее). То есть вызов функции будет примерно таковым:
(defun lispru-line-create (owner start end lst / res doc)
;|
* Функция создания отрезка
* Параметры вызова:
owner указатель на пространство-владелец.
nil -> пространство модели текущего документа;
start координаты начальной точки отрезка в WCS
end то же, конечной
lst список дополнительных параметров вида:
'(("layer" . <Имя слоя>) ; nil -> Текущий
("color" . <Номер цвета ACI>) ; nil -> текущий
("lweight" . <Вес линии>) ; nil -> текущий
("ltype" . <Тип линии>) ; nil -> текущий. Если не nil, то должен
; быть уже в файле. Иначе устанавливается
; текущий
)
* Возвращает vla-указатель на созданный объект или nil в случае ошибки
* Дополнительные ограничения:
текущий слой не должен быть заблокирован
слой, на который помещается объект, должен уже существовать; иначе
устанавливается текущий слой
создание объекта внутри внешней ссылки возможно, но сохраняться изменения
не будут
не разработан механизм установки TrueColor
|;
(if (not owner)
(setq owner (vla-get-modelspace
(vla-get-activedocument (vlax-get-acad-object))
) ;_ end of vla-get-ModelSpace
) ;_ end of setq
) ;_ end of if
(setq doc (vla-get-document owner)
lst (mapcar
(function (lambda (x) (cons (strcase (car x) t) (cdr x))))
lst
) ;_ end of mapcar
res (vla-addline
owner
(vlax-3d-point start)
(vlax-3d-point end)
) ;_ end of vla-AddLine
) ;_ end of setq
(foreach item ((lambda (/ _res)
(foreach _item lst
(setq _res
(cons
(cond
((= (car _item) "lweight")
(cons "lineweight" (cdr _item))
)
((= (car _item) "ltype")
(cons
"linetype"
((lambda (/ _lst)
;; Проверяем наличие указанного типа линии в файле
(vlax-for _x (vla-get-linetypes doc)
(setq
_lst (cons (strcase (vla-get-name _x))
_lst
) ;_ end of cons
) ;_ end of setq
) ;_ end of vlax-for
(if (member (strcase (cdr _item)) _lst)
(cdr _item)
(vla-get-name (vla-get-activelinetype doc))
) ;_ end of if
) ;_ end of lambda
)
) ;_ end of cons
)
((and (= (car _item) "layer")
(vl-catch-all-error-p
(vl-catch-all-apply
(function
(lambda ()
(vla-item (vla-get-layers doc)
(cdr _item)
) ;_ end of vla-item
) ;_ end of lambda
) ;_ end of function
) ;_ end of vl-catch-all-apply
) ;_ end of vl-catch-all-error-p
) ;_ end of and
(cons (car _item)
(vla-get-name (vla-get-activelayer doc))
) ;_ end of cons
)
(t _item)
) ;_ end of cond
_res
) ;_ end of cons
) ;_ end of setq
) ;_ end of foreach
_res
) ;_ end of lambda
)
(vl-catch-all-apply
(function
(lambda ()
(vlax-put-property res (car item) (cdr item))
) ;_ end of lambda
) ;_ end of function
) ;_ end of vl-catch-all-apply
) ;_ end of foreach
res
) ;_ end of defun
В результате подобного подхода вполне правомерными становятся вызовы, например, такие:
(lispru-line-create nil (getpoint) (getpoint) '(("layer" . "1234") ("color" . 11) ("ltype" . "hidden")))
(lispru-line-create nil (getpoint) (getpoint) '(("layer" . "1234") ("color" . 11)))
Как видим, с точки зрения LISP’a все требования соблюдены: количество и последовательность параметров неизменны. Но вот “переменность” последнего параметра дает неимоверную гибкость и удобство. Ведь теперь можно, например, написать функцию создания отрезка, например, внутри указанного блока (или, как ее назвали авторы в книге “САПР на базе AutoCAD – как это делается” – функция-”обертка”). Такой функции можно уже передавать только 3 параметра: указатель на описание блока; координаты начальной и конечной точек. Остальные параметры будут “предустановлены” в соответствующие значения (например, слой всегда “0″, тип, вес и цвет линии – по блоку).
Если хотите, по аналогии с представленным кодом Вы можете сделать, например, функцию создания окружности (там есть парочка подводных камней, связанных прежде всего с системами координат). И сделать несколько “оберток” для нее. Это несложно.
—
Небольшое дополнение: компьютеру в принципе-то все равно – обрабатывать числа или строки, а вот человек обычно текст воспринимает более осмысленно. То есть ключи в списке лучше бы задавать текстовыми: “height”, “width”, “угол” и т.п. Лично я предпочитаю указывать английские слова – они больше согласовываются с официальной справкой, и ориентироваться в них становится легче.
Материалы для проектирования, работы и разработки (и не только в AutoCAD)
Комментарии
Есть 1 комментарий к “О параметрах вызова и “перегрузке” lisp-функций”Трэкбэки
Узнайте, что другие говорят про эту заметку...[...] какое-то количество параметров (подробнее см. О параметрах вызова и “перегрузке” фунций). Но существуют еще и т.н. локальные переменные. Они [...]