1、Autolisp 编程心得.txt 如果不懂就说出来,如果懂了,就笑笑别说出来。贪婪是最真实的贫穷,满足是最真实的财富。幽默就是一个人想哭的时候还有笑的兴致。1.养成良好的书写习惯众所周知,Lisp 是一种表结构语言,括号必须成对出现,在调试时往往为遗漏了一个括号大费周折,所以,养成一个好的编程习惯是学好 Lisp 所必须的。选择一个较好的编辑器,这是一个基本条件,建议使用 Visual Lisp 编辑器或Lisplink 等专用编辑器,此类编辑器可以对函数突出显示。按 Lisp(DCL)专有格式书写,并经常对程序进行“格式化” ,及时发现语法错误,并有利于调试是查找错误。使用自定义函数,并辅
2、助以适当得注释,在较大程序中按功能使用自定义函数可以使得程序条理化。2.函数中循环与转移的使用在高级语言中一般有类似“goto”的语句实现转移,在 AutoLisp 中没有转移的函数。我们可以使用自定义函数实现转移,用 if 及 cond 辅助实现条件转移。当我们需要实现在满足一定条件时进行循环的功能,一般使用 while 函数,但有时需要判断的条件较复杂时,使用 while 函数往往不能实现或使得程序不够简洁。这时我们可以使用“转移” ,将需要实现的功能作为(子)函数,使用恰当,可以在程序中任意“转移” 。一般认为,当一段代码在不同处重复使用时,我们才会使用子函数定义,其实,利用函数的更大的
3、优点是使得程序更加结构化。这就使得我们不必拘泥于程序中的循环语句,而使用函数的循环调用,辅助适当的判断,实现“转移” ,如 A-B-A。当然也可以进行自身调用,构成一个“标准的”循环。如例一中, “程序执行完毕返回”与“空选返回”两种情况如果使用循环语句,其条件是完全不同的,而将函数本身作为子函数调用,程序简洁明了。3.initget 函数中关键字“ ”(空格)的使用空格可以被用作关键字,一般多用来定义鼠标右键退出。当用户输入函数不支持控制位(如 entsel、nentsel、nentselp)时,可直接使用“(initget “ “)”。当用户输入函数支持控制位(如 getpoint 等)时
4、,可使用“(initget 1 “ “)”禁止空输入,而将回车等空输入作为关键字使用。当同时使用其它关键字时,应该将空格作为一系列关键字的最后一个,用“(initget “C “)”(两个空格)调用,否则无效。见例一。*;例一(defun ett_ct()(initget “C “) ;关键字“C”及空格(setq s0 (entsel “n 设置颜色 C / 选取文本:“)(cond( (= s0 “C“) (ett_col) ;转设置颜色子函数( (= s0 “) nil) ;空格退出( (and (= (type s0) LIST) ;选择实体(= (cdr (assoc 0 (entg
5、et (car s0) “TEXT“) ;判别文本). ;操作内容(ett_ct) ;编辑后返回选择)(t (ett_ct) ;空选返回选择)*有时需要进行复杂的判断,使用如“(= s0 “)”语句可能不能准确判别输入的空格关键字与空选择,可以使用“(= (type s0) STR)”语句。4.Lisp 的暂停与 while 的特殊使用Lisp 一般在交互输入时才会暂停,如果只需要实现屏幕显示暂停,可使用 grread 函数,grread 函数对所有合法的输入设备均会作出反应,有时我们只希望对键盘有反应,可使用while 函数进行循环。*(princ “nPress ENTER to cont
6、inue:“)(while (/= (car (grread) 2)*while 用于满足一定条件的循环,其标准语法为: (while testexpr expr.) 其中 expr 解释为“在 testexpr 为 nil 之前要求值的一个或多个表达式” ,为可选项(在 R14 之前没有方括号,但仍为可选项) 。正常我们使用 while 时,总会有 expr 项,更多的时候,我们是为了 expr 项才会使用这种循环语句,所有我们往往有 expr 项是不可缺少的感觉。这里我们使用 while 函数的语法是 while 函数的特例,即没有 expr 项的情况。如果希望对鼠标右键同时反应,可以使用
7、:*(princ “nPress ENTER to continue:“)(while (and (/= (setq a(car (grread) 2) ;键盘(/= a 11) ;鼠标右键(SHORTCUTMENU0)(/= a 25) ;鼠标右键(SHORTCUTMENU0))*5.输入距离Lisp 语言中输入距离的函数为 getdist,但我们有时需要输入负值,有时需要在输入距离的同时得到角度,使用 getdist 函数就显得无能为力,这时,我们可以灵活使用其它交互输入函数如 getpoint、getcorner 等,通过计算得到我们所需要的值。例二是一段输入长度的同时得到默认角度的代码
8、,使用 getpoint 函数。*;例二(setq pt0 (getpoint “n 直线基点: “)pt1 (getpoint pt0 “n 直线长度: “) ;长度及角度可用键盘或鼠标定位dst (distance pt0 pt1) ;计算长度ang (angle pt0 pt1) ;计算默认角度ang1 (getangle pt0 (strcat “n 直线方向: “)*例三是可以按阵列方式输入行列间距的代码,输入距离为正值,修改部分代码可输入负值,使用 getcorner 函数,同时使用 initget 的控制位 128。*;例三(defun lc_dist ()(initget 12
9、8) ;允许任意输入(setq disr (getpoint “n 指定单位单元或输入行间距: “)(if (= (type disr) LIST) ;鼠标输入(progn(initget 1)(setq dis (getcorner disr “n 指定对角点: “) ;鼠标输入对角disc (abs (- (car dis) (car disr) ;正值行距disr (abs (- (cadr dis) (cadr disr) ;正值列距) ;计算行列间距)(if (= (type disr) STR) ;键盘输入行距(if (setq dis (distof disr) ;判断输入的是否
10、距离(progn(initget 6)(setq disc (getdist “n 输入列间距: “) ;输入列距)(progn ;键盘输入格式不符返回(princ “n 需要正数值或两个二维角点。“)(lc_dist)(progn ;空输入返回(princ “n 需要正数值或两个二维角点。“)(lc_dist)*6.数学运算函数的数量界限在 Lisp 中对表中数据进行求和、求最大值等数学运算时,往往直观的对表直接赋予运算函数,使用语句如“(eval (cons MAX numlist)”,一般都可以进行计算,但当表中数据数量大于 255 时,将会出现错误“bad argument value
11、: does not fit in byte: 256” 。对于这种情况,我们不必对数据表进行分段,可以直接使用函数 apply,语法更简单:(apply MAX numlist)。apply 可将数据表传送给指定的函数进行求值而不受数据数量的影响。 受表中数据数量影响的数学运算函数有:+、-、*、/、max、min、logand 及logior。7.选择集与表选择集是一种特殊结构的表,只能通过特定的函数进行操作,但这些函数对大量重复的操作只能通过循环实现,显得力不从心,不能体现 Lisp 语言表结构的优越性。其实我们只要通过存取实体名或实体句柄,将它们存为一个普通结构的表,完全可以通过常规表
12、操作函数实现对实体的操作。例四是一段使用 apply、mapcar 函数联合求文本选择集中文本基点最大 y 值得代码,只是一个示例,如果结合 VL-sort 函数,可轻松实现对文本的排序。*;例四(setq sl nil i -1)(repeat (sslength (setq ss (ssget (0 . “TEXT“) ;选择文本(setq i (1+ i)en (ssname ss i) ;从选择集中取出文本sl (cons en sl) ;构造包含实体名的表)(setq maxy (apply max ;求文本基点最大 y值(mapcar(lambda (x)(caddr (assoc
13、 10 (entget x) ;提取 y 值)sl)*当然,选择集也有其优势的一面,比如对选择集中实体的删除操作非常简单、选择集中的实体不会重复及选择集可以与 Acad 命令交互使用等特征是一般表所不具备的,所以,编程时应根据程序要求,灵活运用。8.cal 的使用与加载Acad 随机附带了一些外部定义命令,其中 cal(计算器)命令是最常用的命令之一,在加载 gromcal.arx 后 cal 可以在 Lisp 程序中像其它函数一样使用,这就使得我们在程序中对文本的四则运算处理变得简单,如“(cal “1+2/3“)”,其中字符串“1+2/3“可以从图形的文本中提取,也可以是符合 cal 要求
14、格式的任一字符串(详见 Acad 联机帮助) 。需要注意的是,在 Acad 中 gromcal.arx 只能加载一次,重复加载将使 Acad 以外退出(无提示) 。需要使用 cal 函数的 Lisp 程序,应在程序尾部加上以下代码:*(if (or (= (type c:cal) LIST) ;R14 使用(= (type c:cal) SUBR) ;R2000+使用)(arxload “geomcal.arx“)*9.Undo 处理一个完善的程序应该有较好的出错处理,这是在所有 Lisp 教材上都提及的,但程序的Undo 处理就说得很少或没有提及。其实 Undo 处理对程序来说也是非常重要的
15、,尤其对有较多输出的复杂程序而言,不能解决 Undo 问题,使用起来会极不方便。对于 Undo 问题的解决,一种方法是尽量少用或不用 command 函数,即不调用原始命令,这是一种较好的方法,但必须注意的是,一段程序必须至少有一次调用 command 函数,否则Undo 命令将取消程序运行前的前一次命令,解决的方法是在程序运行的起始位置加一个无谓的 command,如“(command “color“ “)” 。有时不使用 command 函数不能达到我们要求的一些功能,或使得程序过于复杂,我们可能需要使用一些 command 函数(原始命令) ,这是就应该在程序中进行 Undo 处理,即使
16、用Undo 命令的编组功能。例五是一段程序出错函数与 Undo 处理的示例。*;例五(defun newerr (s) ;出错函数(if s(progn(term_dialog) ;使用对话框时使用(if olderr (setq *error* olderr) ;出错函数恢复(if oldvar (setvar . oldvar) ;系统变量恢复(if olderr (setq *error* olderr) ;出错函数恢复(command “_.undo“ “_e“) ;Undo 编组结束)(princ)(defun c:my(/ .) ;主程序(主函数)(setvar “cmdecho“
17、 0) ;取消命令回显提示(command “_.undo“ “_BE“) ;Undo 编组开始(setq olderr *error* *error* newerr) ;调用自定义出错函数(setq oldvar (getvar .) ;保存相关系统变量(setvar . ;设置系统变量. ;程序段.(setvar . oldvar) ;恢复系统变量(setq *error* olderr) ;恢复出错函数(command “_.undo“ “_E“) ;结束 Undo 命令编组(princ) ;取消程序返回值)*10.程序调试是块注释的使用我们经常会加上或屏蔽一段代码辅助程序调试,此时最常用的是在需要暂时屏蔽的代码前使用行注释符号“;” ,对于较多的代码就需要使用块注释“;|;” ,如果一段代码需要频繁屏蔽,将行注释与块注释组合使用,可以带来极大方便。