О параметрах вызова и “перегрузке” 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", "угол" и т.п. Лично я предпочитаю указывать английские слова - они больше согласовываются с официальной справкой, и ориентироваться в них становится легче.

Размещено в Код LISP · Метки:



Комментарии

Есть 1 комментарий к “О параметрах вызова и “перегрузке” lisp-функций”

Трэкбэки

Узнайте, что другие говорят про эту заметку...
  1. [...] какое-то количество параметров (подробнее см. О параметрах вызова и “перегрузке” фунций). Но существуют еще и т.н. локальные переменные. Они [...]



Поделитесь своим мнением


Я не робот.