О параметрах вызова и “перегрузке” lisp-функций
Известно, что в "нормальных" языках программирования (таких, как Visual Basic, C#, C++ и т.п.) есть возможность создавать так называемые перегруженные функции, или функции с разным количеством и / или типом параметров. В AutoLISP / VisualLISP подобной возможности не предусмотрено. То есть если пишется функция создания, например, отрезка, то ей должны передаваться сразу все параметры: начальная и конечная точки, пространство-владелец создаваемого объекта (точнее, указатель на него), тип линии, цвет, и еще безумное количество устанавливаемых свойств. Запомнить их последовательность нереально, особенно учитывая тот факт, что в VLIDE напрочь отсутствует технология IntelliSence. Но не все так плохо. Обойти это ограничение можно и нужно. Об этом и поговорим.
Разве кто-то мешает нам воспользоваться тем, что LISP ориентирован на обработку списков? По-моему, никто. А раз так, то и будем передавать в ту же функцию пару-тройку параметров, один из которых - список, сформированный по заранее определенным правилам.
Рассмотрим пример создания отрезка. Нам гарантированно надо передавать указатель на пространство-владелец создаваемого объекта; координаты начальной точки; координаты конечной точки; и... И все, остальные свойства должны приниматься по какому-то правилу (к примеру: если не указывается, то слой принимать текущим; если не указывается, то тип линии - текущий и так далее). То есть вызов функции будет примерно таковым:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | (vl-load-com) (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 |
В результате подобного подхода вполне правомерными становятся вызовы, например, такие:
1 2 3 | (lispru-line-create nil (getpoint) (getpoint) '(("layer" . "1234") ("color" . 11) ("lweight" . 50) ("ltype" . "hidden"))) (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", "угол" и т.п. Лично я предпочитаю указывать английские слова - они больше согласовываются с официальной справкой, и ориентироваться в них становится легче.
Комментарии
Есть 1 комментарий к “О параметрах вызова и “перегрузке” lisp-функций”Трэкбэки
Узнайте, что другие говорят про эту заметку...[...] какое-то количество параметров (подробнее см. О параметрах вызова и “перегрузке” фунций). Но существуют еще и т.н. локальные переменные. Они [...]