autolisp虽好,奇怪问题也不少
自从1986年首次将autolisp引入,autolisp在autocad中已经效力快40年了。 这几十年间,autocad经历了几十个大版本的升级,然而,其lisp解释器却并没有一同升级增强。 目前最新的autocad2022,其lisp解释器vllib仍然停留在1999年,只是在autocad2022发布的时候, 重新编译了一下而已。
看看下面这个截图,发布时间相隔10年的两个autocad,其lisp解释器的差异只有一个编译号。

长时间的冷落,自然也导致有些问题迟迟得不到解决,多少有点鸵鸟策略的味道。就个人使用经历来看, autocad目前的lisp解释器、编译器存在的问题可能难以发现,有些可能和语言特性有关,有些则真的像是bug。 对我来讲,最头疼也是耗时最久才解决的,主要有两个问题。
形参冲突
一般来说,形参是最不应该出问题的地方,然而,事实就是这么残酷,它恰恰就出问题了。
发现这个问题纯属偶然。去年年初,尝试着自己实现一遍vl-*系列函数,在尝试的过程中,就发现了这个问题。 出问题的,就是下面这段代码。
|
|
上述代码,语法上没有问题,一般认为不应该会有什么问题。最后两行,期望的返回值分别是'(1 2 3 4 5 6)和'(0 0 0 0)。
事实上,上述代码在common lisp 和racket/scheme环境下,返回值是正常的,就是期望的结果。
common lisp的运行结果如下:

racket的运行结果如下:

但是,上述代码的最后一行,在autolisp环境下会陷入死循环进而出错终止。思考了很久,尝试了很多办法, 都没能解决问题。后来,在autodesk官方的论坛里边,发帖求助,终于,得到了网友的帮助,说是形参冲突, 因为xg-remove-if-not里边的形参与xg-remove-if同名,修改形参名之后就能得到期望的结果了。大概可以改成下面这样子。
特定情况下出现编译过程卡死
曾经长时间使用autocad 2008,至今仍然认为2008版是最为流畅的版本。自从用了win 10之后,就换成了autocad 2012了, 因为2012版在当时是最流畅的,另外,网上流传的64位autocad 2008缺乏第三方插件的支持。
编译卡死的问题,就出现在2012版,因为手头上最老的就是这个版本。
这个问题也是花了很久才想清楚,前前后后差不多两三年时间。具体问题就是,在lisp源文件编译时, 链接模式选择为“链接”或者“内部链接”,假如源文件中有很多局部化的函数(位于顶层函数内部, 并且将其函数名设置为局部变量的函数),那么,就有很大概率在编译时出现卡死。
对于这个问题,当时的第一反应是,也许源文件列表中的顺序有问题?花了很多时间检查,调整了很多次, 最终还是无功而返。由于在更高版本的autocad上,比如2016 、2022版,可以正常编译,索性就懒得去管它了。
最近几个月花了不少时间重构,经常使用visual lisp ide的代码检查功能,在检查之后的回显信息中, 函数被局部化了就是作为警告信息回显的。在看了无数次的警告信息之后,突然有一天开悟了,把解决编译卡死的 思路转到警告信息上了。于是,花时间把局部函数移到顶层了,再次进行编译时,一切又恢复正常了。困扰多年的老问题, 就这样解决掉了。
结语
前面说的第一个问题,算起来可能并不是bug,毕竟,autolisp作为老派的lisp代表,动态作用域是它的特色。 动态作用域虽然历史悠久,有其大批拥趸,也还是容易导致形参命名冲突这一类问题的出现,这样的问题一旦出现了, 即使定位准了可能也难以找到解决方案。对autolisp来说,这种情况更加地棘手,因为没有命名空间, 没有隔离机制,在代码量达到一定程度之后,命名冲突似乎难以避免。
第二个问题,可能真的算是编译器的bug了。因为,在“标准”编译模式下,不对函数进行链接的时候,问题就不存在了。 所以,问题就出在编译优化上,强行对某些不该链接的函数进行链接,可能就导致了问题。
另外,需要特别注意的是,autolisp提供了名为function的函数,用这个函数包裹lambda函数之后, 在编译阶段也可以对lambda这种匿名函数进行优化。这种做法,也可能导致某些莫名其妙的问题, 使用function还是要小心为好。
文章作者 Jack Hsu
上次更新 2022-02-22
许可协议 Copyright © Jack Hsu. All Rights Reserved.