本帖最后由 beverage 于 2012-3-5 17:40 编辑
开始对Lisp程序感兴趣已经有了一段时间,在不断入门的过程中,在网上看了好多高手的经验和介绍,在此深表感谢,因为过去几个程序中时常用到*error*这个函数,小小的研究了一下,贴出一些心得,希望大家指教。下面的原文,是从各位高手的发帖中总结或领悟而出,如果创新性不够,请大家海涵
1,我们为什么要用到*error*函数
首先,我们要了解这样一个规则,就是在AutoCAD2000以及以上的版本中,在程序出错的时候,会清除所有去不变量的定义。下面是一个例子:
- (defun tt (/ B C)
- (setq A "A"
- B "B"
- C "C"
- )
- (exit)
- )
程序回显的提示是“;错误:quit / exit abort”,请暂时记住这一点,一会我们还要用到。
执行tt之后查询各变量的数值,有这样的结果 A的值是”A”,而B和C的值则是nil。
既然在出错的时候,AutoCAD会清除所有的局部变量的定义,那么我们为什么还要纠结*error*函数呢?
在程序执行的过程中,我们可能会改变一些系统变量的数值,比如dimzin以改变数字的格式,改变osmode以改变捕捉的状态,这里顺便提一下,关闭捕捉不是简单的将osmode设置为0,osmode为0时表示的是清除所有的捕捉点而不是关闭捕捉,关闭捕捉是由位码 16384 (0x4000)来控制的,详见AutoCAD关于osmode的帮助。在改变了这些系统变量的数值之后,一般负责任的编制程序的人,会在程序退出之前予以恢复,否则将是程序的友好性大打折扣。但是,往往程序按照我们自身的期望顺利执行的几率即便不是很小,也不是我们在考虑恢复系统变量等的问题是应该考虑的主要问题。这时,我们就需要用到*error*这个函数。
2,我们要把*error*做为一个局部变量
按照上面一条中提到的规则,我们可以把*error*做为我们程序的局部变量,这样,在程序出错而没有正常运行的时候,我们不需要太大的经历来恢复*error*函数本身。
我们首先定义一个新的*error*函数,为了测试的效果,我们让他执行这样的功能:不显示任何回显信息而是显示我们自己的相关提示,这样,我们可以很容易的区分出我们的*error*函数的作用域。
- (defun *error* (msg)
- (princ "\n自定义出错函数")
- )
然后,将这个函数加入我们上面的主程序中,虽然主程序只有一个强行退出的函数,但是我们根据各个函数的关系,依然把他成为主程序。
- (defun tt ( / *error* B C)
- (defun *error* (msg)
- (princ "\n自定义出错函数")
- )
- (setq A "A"
- B "B"
- C "C"
- )
- (exit)
- )
本来按照正常的情况这段语句应该回显“;错误:quit / exit abort”但是因为我们改变了*error*函数,所以这段语句回显的是“自定义出错函数”,这虽然看起来很成功,但不是这段语句本身的意义,而这时我们再次键入(exit),AutoCAD将按照正常的*error*函数执行,也就是回显“;错误:quit / exit abort”,因为我们自定义的*error*函数已经在tt执行出错的时候倍按照局部变量的处理原则,被重置了。
3,利用*error*函数来恢复我们改变的系统变量
利用*error*函数来恢复我们改变的系统变量才是我们研究这个函数用法的初衷,期间需要配合其他函数来执行相关的取得初始的系统变量和恢复变量的工作,这里只做简要的说明,而详细的用法请大家关注相关的函数。
首先用下面这个语句来获取系统的初始变量,一样的我们可以把e-lst设定为局部变量。
- (setq e-lst (mapcar (function (lambda (n) (list 'setvar n (getvar n))))
- '("autosnap" "osmode" "aperture" "hpspace" "hpassoc" "mirrtext" "auprec" "luprec" "dimzin" "cecolor")))
'("autosnap" "osmode"...)这里定义了一个由众多系统变量组成的list,这里完全可以根据你的需要做增加或者减少,所定义的lambda函数定义了一个类似这样的list:(('setvar "autosnap" autosnap的值)('setvar " osmode " osmode的值)...),
实话实说,这也是我从别人的代码中复制出来的,而恢复系统变量则可用下面的语句来执行:
- (mapcar 'eval e_lst)
这里面的eval的用处其实很强大,如果有兴趣可以试试
(mapcar 'eval (cons '(alert "弹出消息") e_lst))其中的'(alert "弹出消息")可以改为任何你需要*error*中执行的子例程。
然后为了过滤一些我们不喜欢的回显消息,增加这样的语句
- (if (not (member
- msg
- '(nil "函数被取消" ";错误:quit / exit abort")
- )
- )
- (princ (strcat ";错误:" msg))
- )
这样可以过滤掉"nil","函数被取消", ";错误:quit / exit abort"的回显。
在程序完成的最后,正常执行程序的情况下,我们依然需要将我们改变的系统变量恢复,可以用不带参数的*error* 来执行系统变量的恢复
- (*error* nil)
按照上面的说法,我们构件了下面这段对于系统变量安全的代码:
- (defun c:tt( / *error* e_lst)
- (setq e_lst (mapcar (function (lambda (n) (list 'setvar n (getvar n))))
- '("autosnap" "osmode" "aperture" "hpspace" "hpassoc" "mirrtext" "auprec" "luprec" "dimzin" "cecolor")))
- (defun *error* (msg)
- (mapcar 'eval e_lst)
- (if (not (member
- msg
- '(nil "函数被取消" ";错误:quit / exit abort")
- )
- )
- (princ (strcat ";错误:" msg))
- )
- )
- ;程序代码
- (*error* nil)
- )
为了测试上面的代码,其实经过上面一步一步的测试,我们应该可以肯定上述代码可以满足我们对于系统变量恢复的各种要求,为了完整起见,我们可以用下面的代码来进行测试,为了看出效果,尤其是使用英制单位的各位,请对或者你的系统变量,或者下面程序的代码中的"dimzin"进行设置。
下面的代码中,我们虚拟的把"dimzin"系统变量设置为3,然后构建了一个等待用户输入的getpoint,这样你就有机会按下ESC,以体验出错时的情形
- (defun c:tt( / *error* e_lst)
- (setq e_lst (mapcar (function (lambda (n) (list 'setvar n (getvar n))))
- '("autosnap" "osmode" "aperture" "hpspace" "hpassoc" "mirrtext" "auprec" "luprec" "dimzin" "cecolor")))
- (defun *error* (msg)
- (mapcar 'eval e_lst)
- (if (not (member
- msg
- '(nil "函数被取消" ";错误:quit / exit abort")
- )
- )
- (princ (strcat ";错误:" msg))
- )
- )
- (setvar "dimzin" 3)
- (getpoint "\n即时你在这里取消,dimzin的系统变量依然可以恢复.")
- (*error* nil)
- )
到此结束,谢谢各位围观收藏!
如果未得到来自Dwg@live.com的同意转载的邮件,请勿转载。 |