Lee Mac大神为什么这样写vla容器的函数?
Lee Mac大神在写获取vla容器的时候,一般写成这种格式:(defun LM:acdoc nil
(eval (list 'defun 'LM:acdoc 'nil (vla-get-activedocument (vlax-get-acad-object))))
(LM:acdoc)
)
而不是传统的
(defun LM:acdoc nil
(vla-get-activedocument (vlax-get-acad-object))
)
这样写有什么好处吗?是不是为了同一个doc在多次调用的时候,避免重复求值?
本帖最后由 highflybird 于 2019-8-1 00:13 编辑
首先我的个人观点觉得这个地方完全是Lee秀技巧有点过了,但实际并未达到他的目的。
1、分析他的这段代码:
(defun LM:acdoc nil
(eval (list 'defun 'LM:acdoc
'nil
(vla-get-activedocument (vlax-get-acad-object))
)
)
(LM:acdoc)
)
第一段 :
(eval (list 'defun
'LM:acdoc
'nil
(vla-get-activedocument (vlax-get-acad-object))
)
)
从结果来说,完全等同于
(eval '(defun LM:acdoc () (vla-get-activedocument (vlax-get-acad-object))))
也就是下面的语句:
(defun LM:acdoc nil
(vla-get-activedocument (vlax-get-acad-object))
)
也就是说 eval,eval的是对一个函数进行定义,仅仅是定义,但并未对这个函数求值。
2、但从效率来说,Lee的反而低了点,因为用了eval
所以 ,Lee的这段代码就等于下面的:
(defun LM:acdoc nil
(defun LM:acdoc nil
(vla-get-activedocument (vlax-get-acad-object))
)
(LM:acdoc)
)
3、这这个函数粗一看,是递归的,递归没做处理,就会变成死循环,但是为何没有呢?
是因为 Lee 定义了这个名为 LM:acdoc的函数,结果在函数内部又重新定义了一下这个函数
所以函数返回的时候,用的却是函数里面的定义。
以一个简单的例子来说
(defun A ()
(defun A ()
(+ 1 1)
)
(A)
)
我开始定义了一个名为A 的函数,在函数里面又调用了A ,如果在函数内没有重新定义A函数,这个函数将陷入死循环,很快LISP就会有错误提示:
vlide 出现硬错误 ***
已达内部堆栈限制 (模拟)
说明是递归函数没设置终止条件。但正是因为在函数里面重新定义了函数体,所以才避免的这样情况,最后执行的是在函数内的定义。
4、Lee 的本义是只是将下面的语句执行一次求值:
(vla-get-activedocument (vlax-get-acad-object))
但是达到效果了吗?没,
用户可以自行用断点运行,发现结果还是要进行求值,所以Lee的想法落空了。
因此,就这个函数来说,我个人观点认识是Lee的做法有点过了。
nzl1116 发表于 2019-8-1 12:28
我被你搞偏了,你那个(alert "a")就是 nil
不管怎么说,就是用函数储存,函数要执行defun表达式,而用变 ...
我说的肯定是对的
(eval (list 'defun 'LM:acdoc '() (alert "a") (vla-get-activedocument (vlax-get-acad-object))));先计算(alert "a"),返回nil
(eval (list 'defun 'LM:acdoc nil nil (vla-get-activedocument (vlax-get-acad-object))))
;再计算vlax-get-acad-object,返回对象
(eval (list 'defun 'LM:acdoc nil nil (vla-get-activedocument #<VLA-OBJECT IAcadApplication 00007ff71ec6c910>)))
;再计算vla-get-activedocument,返回对象
(eval (list 'defun 'LM:acdoc nil nil #<VLA-OBJECT IAcadDocument 0000000030636888>)
;然后list得到一个表(DEFUN LM:ACDOC nil nil #<VLA-OBJECT IAcadDocument 0000000030636888>),这个表里的都是值,不再是代码
再然后就是重新定义函数,这个函数里的只有值,你再调用函数时肯定不会再计算了,直接就返回值了.
他就是把值存在了函数里
就相当于
(defun LM:ACDOC ()
"a"
1
nil
2
)
这个函数里只有值 ,不可能有其它东西
你再调用的时候,他只返回最后的1个值,不管你这个函数前面有什么值
本帖最后由 mayingjun 于 2019-8-1 10:30 编辑
highflybird 发表于 2019-8-1 00:02
首先我的个人观点觉得这个地方完全是Lee秀技巧有点过了,但实际并未达到他的目的。
1、分析他的这段代码: ...
我把程序的格式调整成这样。
(defun LM:acdoc nil
(eval
(list 'defun 'LM:acdoc 'nil
(alert "aaaa")
(vla-get-activedocument (vlax-get-acad-object))
)
)
(LM:acdoc)
)
并在 (vla-get-activedocument (vlax-get-acad-object)) 的最后)处设置了断点,发现第一次运行时,alert和断点被触发,返回值是:#<VLA-OBJECT IAcadDocument 0e9db808>。再次执行时,两个都没有触发,直接得到了#<VLA-OBJECT IAcadDocument 0e9db808>。看来这种写法还是有作用的。
而写成你说的那种方式
(defun LM:acdoc nil
(defun LM:acdoc nil
(alert "aaaa")
(vla-get-activedocument (vlax-get-acad-object))
)
(LM:acdoc)
)
则再次运行的时候,alert和断点都会被触发。
可能只是为了新鲜 天高任鸟飞,确实厉害 本帖最后由 琴剑江山_10184 于 2020-2-22 19:34 编辑
highflybird 发表于 2019-8-1 00:02
首先我的个人观点觉得这个地方完全是Lee秀技巧有点过了,但实际并未达到他的目的。
1、分析他的这段代码: ...
;;;;;;
highflybird 发表于 2019-8-1 00:02
首先我的个人观点觉得这个地方完全是Lee秀技巧有点过了,但实际并未达到他的目的。
1、分析他的这段代码: ...
那要想达到一次求值的目的,是否只能用全局变量了? 本帖最后由 highflybir 于 2019-8-1 10:31 编辑
mayingjun 发表于 2019-8-1 08:52
那要想达到一次求值的目的,是否只能用全局变量了?
就这种情况来说,应该是要用全局变量。其实,用函数也没关系,这个地方求值对于vlisp内部来说,求过一次值后,就不会再去eval,因为vlisp给你返回的是一个引用,类似于指针。 vla-get 与 vlax-create是不同的。因为这两个都是用get方式创建的。
所以,不管你用 多少次(vlax-get-acad-object) 和(vla-get-ActiveDocument (vlax-get-acad-object)),得到的结果是相同的.
就像下面的样子:
#<VLA-OBJECT IAcadApplication 00007ff6634dc910>
#<VLA-OBJECT IAcadDocument 0000000037039cc8>
而用vlax-create-object得到的是不同的引用。
highflybir 发表于 2019-8-1 10:24
就这种情况来说,应该是要用全局变量。其实,用函数也没关系,这个地方求值对于vlisp内部来说,求过一次 ...
看我上面的测试,lee mac这种写法还是有效的。 本帖最后由 highflybir 于 2019-8-1 10:42 编辑
mayingjun 发表于 2019-8-1 10:31
看我上面的测试,lee mac这种写法还是有效的。
嗯,看来lee是对的,我再仔细看看eval那段.看来,((eval
'(defun
LM:acdoc
nil
(alert "bbb")
(vla-get-activedocument (vlax-get-acad-object))
)
)
和
(eval
(list 'defun
'LM:acdoc
'nil
(alert "bbb")
(vla-get-activedocument (vlax-get-acad-object))
)
)并不相等。