☰
AutoLISP函数分类速查:从数学运算到选择集处理的编程实践
2026/10/15 10:49:31 网站建设 项目流程

简介:面向AutoCAD二次开发与LISP编程学习者,这是一份AutoLISP常用函数分类速查手册。内容按数学运算、检验与逻辑运算、转换运算、列表处理等模块编排,覆盖(+、-、*、/)、1+、1-、abs、expt、sqrt等数学函数,=、>、<、and、eq、equal等逻辑判断函数,angtof、rtos、trans等转换函数,以及car、cdr、cons、member等经典列表操作函数;每个函数均给出完整的语法格式与返回值说明,便于快速查阅和对比记忆。资源为单个PDF文件,体积仅47KB,轻量便携,可随时离线检索,特别适合新手入门时对照学习,也适合有经验开发者案头备查。目前已有763人浏览学习。通过该手册,读者能在编写AutoCAD脚本或二次开发程序时快速确认函数用法,减少查找时间,同时借助分类结构加深对LISP函数式写法的理解,从容应对坐标计算、单位换算、批量数据处理等常见场景,提升开发效率。

1. LISP函数大全分类手册:从“查字典”到“会写程序”,这份AutoLISP速查表够用

某个设计单位的老哥,拿到一套别人写的批量标注程序,想改标注前缀,盯着car、cdr、caddr看了半小时没敢动。他不是不会 LISP,是不知道去哪查——在线文档太散、范例太碎。这份按用途分类的 AutoLISP 函数手册,价值就在于把数学、逻辑、列表、字符串、几何、对象、选择集、流程控制、错误处理等十九个大类的函数全摊开,按“我要干什么”去查,而不是按字母去翻。新手拿它当索引,熟手拿它对照参数边界,写程序时翻一页就能落地。

2. 数学与逻辑运算:先把数值、比较和位运算的底层逻辑捋顺

AutoLISP 的数学函数看着简单,但踩坑点全在细节:整数除法的截断、三角函数的角度单位、fix到底是“截断”还是“四舍五入”。这一章把这些边界一次说清。

2.1 数学函数:从加减乘除到 sqrt、expt 的精度与弧度细节

最基本的四则运算,很多初学者以为(/ 17 5)会返回 3.4,实际返回 3。AutoLISP 里两个整数相除,结果是整数,小数部分直接舍掉,不是四舍五入。想让结果带小数,至少把一个操作数写成实数:

(setq a 17 b 5) (setq sum (+ a b)) ; 22,加法可传多个参数 (setq diff (- a b)) ; 12 (setq prod (* a b)) ; 85 (setq quot (/ a b)) ; 3,整数除法截断,不是四舍五入 (setq q2 (/ a 2.0)) ; 8.5,除法只要有一个实参,结果就是实数 (setq r (rem a b)) ; 2,余数 (setq g (gcd 12 8)) ; 4,最大公因数 (setq s (sqrt 2.0)) ; 1.41421,平方根

rem和gcd在工程场景里用得不多,但做参数化建模时,比如生成螺栓阵列要判断“能否整除”,这两个函数比手写循环快得多。fix的坑在精度:(fix 3.99)返回 3,它是向零取整,不是四舍五入。想要四舍五入得自己加 0.5 再 fix。

三角函数是重灾区。手册里写着(sin 角度)返回值是“角度值为弧度”,但实际操作中经常有人直接(sin 30)期望得到 0.5,结果得到的是 -0.988,因为 30 被当成了弧度。从度转弧度的标准写法是(* ang (/ pi 180.0)),或者写成(/ (* ang pi) 180.0):

(setq ang 30) ; 30 度 (setq rad (/ (* ang pi) 180.0)) ; 转弧度 (setq y (sin rad)) ; 0.5 (setq x (cos rad)) ; 0.866025 (setq t (atan 1.0)) ; 0.785398,pi/4

atan有个更实用的变体:(atan y x)可以直接返回点相对于原点的方位角,范围是 -pi 到 pi,比(angle '(0 0) (list x y))少一步构造列表。exp、log、expt用的频率不高,但做拟合计算或指数衰减时能顶上。手册里还有个pi常数,约 3.1415926,注意它不用括号,直接写pi。

2.2 比较、位运算与判断函数:=、equal、eq 的区别是重点

逻辑判断函数里,=、eq、equal三者最容易混。手册原文给的解释很简练:=适用于数值和字符串,eq是“实际相同”,equal是“内容相同”。实际项目里判断字符串用=最直接,判断列表内容用equal,而eq是用来判断两个变量是否指向同一个列表对象:

(setq s1 "abc") (setq s2 "abc") (= s1 s2) ; T,字符串内容相等 (equal s1 s2) ; T,内容相同 (setq l1 (list 1 2)) (setq l2 (list 1 2)) (eq l1 l2) ; nil,两个列表不是同一个对象 (equal l1 l2) ; T,但内容完全相同 (equal 3.0 3.0000001 0.001) ; T,带差量比较,浮点误差的后悔药

equal的第三个参数是差量,这是处理浮点比较的唯一正经办法。比如计算两个点是否重合,(= (distance p1 p2) 0)极大概率会得到 nil,因为浮点误差,正确写法是(equal (distance p1 p2) 0.0 1e-6)。我一般会把这种判断封装成一个工具函数,避免每次手写容差。

<、>、<=、>=的用法和=一样,都可以接受多个参数做链式比较:(< 1 2 3)返回 T。位运算里的~是按位取反,boole是低位布尔运算,这两个在工程图里少用,但做图元状态位判断时会遇到。and、or是短路逻辑,(and (setq a (getvar "CMDECHO")) a)这类写法能在一个表达式里同时完成赋值和判断。

if和cond是后面写流程控制的地基。这里先埋一个点:AutoLISP 里只有nil是假,其他任何值包括 0 和空字符串都是真。这跟 C 语言完全不同,新手写(if (= n 0) ...)没问题,但如果写(if n ...)想表达“n 不为零”就翻车了,因为 n 等于 0 时 0 也是“真”。

3. 列表与字符串处理:AutoLISP 数据交换的“主干道”

AutoLISP 之所以叫 LISP,就是因为一切数据都是列表。点坐标是列表,对象信息表是列表,选择集内部结构也是列表。这一章把列表的构建、访问、批量处理讲透,再把字符串和文件读写接上,程序的数据进出就闭环了。

3.1 列表构建与访问:car、cdr、cons、append 的组合

car取第一个元素,cdr去掉第一个元素后返回剩下的列表,cadr是car和cdr的组合写法,取第二个元素。这三个函数看着简单,但在点坐标处理里是标配:

(setq pt (getpoint "\n指定点: ")) ; 返回 (x y z),比如 (5.0 3.0 0.0) (setq x (car pt)) ; 5.0 (setq y (cadr pt)) ; 3.0 (setq z (caddr pt)) ; 0.0 (setq pt2 (list x y)) ; (5.0 3.0),去掉 Z 坐标

注意getpoint返回的是三维点表,如果你要传给只接受二维点的函数,最好用list重新构造。cons是“构造列表”,它把新元素放到列表头部:(cons 1 '(2 3))返回(1 2 3)。append是把多个列表拼接成一个:(append '(1 2) '(3 4))返回(1 2 3 4)。

cons在 AutoLISP 里还有一个特殊用途:构造关联表的键值对。比如图层信息、对象扩展数据,都是用(cons 键 值)的格式存放的:

(setq alist (list (cons "NAME" "BRACE") (cons "THK" 16))) (assoc "NAME" alist) ; 返回 ("NAME" . "BRACE") (cdr (assoc "THK" alist)) ; 16,取出键对应的值

assoc按关键字查找,cdr取出值。取对象图元信息时,(cdr (assoc 0 (entget en)))能拿到对象的类型字符串,这就是后面第 4 章要频繁用的套路。reverse翻转列表、subst替换元素、nth按索引取值是辅助角色,但subst在改图元组码时很好用,比如把半径组码 40 的值替换掉。

3.2 批量处理:foreach、mapcar 与 lambda 的组合

处理一批坐标点或一批对象时,foreach和mapcar是两种思考方式。foreach是“对每个元素做同一件事”,不关心返回值;mapcar是“对每个元素做变换并收集结果”,返回新列表:

(setq pts '((0 0) (1 1) (2 2) (3 3))) (mapcar '(lambda (p) (distance '(0 0) p)) pts) ; 返回 (0.0 1.41421 2.82843 4.24264) (foreach p pts (princ p) (princ "\n") )

lambda在这里是匿名函数,'(lambda (p) ...)里那个引号不能省。mapcar还有个用法是配合apply做聚合计算:(apply '+ (mapcar 'car pts))能求出所有 X 坐标之和。apply把函数作用到列表上,+可以接受多个参数,这个组合在统计坐标范围时非常顺手。

批量处理的核心思想是“先把数据组织成列表,再用 mapcar/foreach 一次性处理”,而不是像其他语言那样写索引循环。AutoLISP 的repeat和while更接近底层操作,但foreach的可读性明显更好。代码审查时,看到foreach基本能立刻明白意图,而while加索引变量还得数几行才能反应过来。

3.3 字符串、文件与转换函数:把外部数据接进来

字符串函数在生产工具里天天用。strcat拼接、substr截取、strlen取长度、strcase转大小写、wcmatch做通配符匹配。wcmatch支持*、?、#等模式,判断文件名或图层名是否匹配前缀比substr更简洁:

(setq fn "BRACE-01.dwg") (wcmatch fn "BRACE*") ; T (wcmatch fn "???-*.dwg") ; T

rtos和angtos是经常被忽略的“数据格式化”函数。把实数转成字符串时,不指定模式会采用当前图形的线性单位设置,导致在不同图纸上跑出不同结果。我一般会显式指定模式:

模式含义示例(数值 12.5)
1科学计数"1.2500E+01"
2小数"12.50"
3工程(英尺-英寸)"1'-0.50""
4建筑(英尺-英寸分数)"1'-0 1/2""
5分数"12 1/2"

写标注类工具时,如果rtos不指定模式,标注文字会跟着图纸单位设置变,这在批量出图时很容易翻车。angtos同理,角度转字符串时要显式指定度和精度。

文件读写方面,open配合read-line、write-line能读写文本文件,最常见的场景是读坐标文件批量画点。注意open的第二个参数是模式字符串,"r" 读、"w" 写、"a" 追加:

(setq f (open "D:/pts.txt" "r")) ; 文件不存在时返回 nil (while (setq line (read-line f)) ; 读到文件尾返回 nil,循环结束 (setq data (read (strcat "(" line ")"))) ; 把 "10,20" 变成可处理的数据 (princ data) (princ "\n") ) (close f) ; 用完必须关

这段代码里(read (strcat "(" line ")"))是把文本行包上括号再解析成列表,常见做法,能省去手工 split 的麻烦。read函数解析字符串为 LISP 数据,但要求字符串本身是合法的 LISP 表达式,所以要先拼一对括号。文件路径建议用正斜杠,反斜杠要写成双反斜杠转义,这是 Windows 路径的老坑。

4. 几何计算与对象操作:把坐标和选择集变成批量改图的能力

前面两章处理的是“数值”和“数据”,从这一章开始接触真正的图形。几何函数负责计算,对象函数负责读写图元,选择集负责批量圈选,三样组合起来才能写出能用的工程工具。

4.1 几何函数配合:angle、distance、polar、inters、osnap

angle返回两点连线与 X 轴的夹角,单位是弧度;distance返回两点距离;polar从基点按角度和距离推算新点。这三个函数配合能解决大部分几何定位问题。比如求线段中点:

(setq p1 (getpoint "\n第一点: ")) (setq p2 (getpoint "\n第二点: ")) (setq d (distance p1 p2)) ; 线段长度 (setq a (angle p1 p2)) ; 线段方位角,弧度 (setq mid (polar p1 a (/ d 2))) ; 从 p1 沿方位角走一半距离

polar的第三参数必须是距离,第二参数必须是弧度。如果手头是角度,得先转换。inters求两条直线交点,它有四个点参数和一个模式参数,模式为 nil 时只返回实际相交的点,模式为 T 时即使延长线相交也返回交点。判断线段是否真的相交,就用模式 nil;判断延长线交点,用 T。

osnap按捕捉模式取点,比如(osnap pt "end")返回 pt 附近端点的坐标。这个函数在模拟“鼠标捕捉”场景里很有用,但要注意它依赖当前图形里的对象,如果 pt 附近没有符合条件的对象,返回 nil。

4.2 对象与选择集:entget、entmod、ssget 的过滤用法

AutoLISP 操作对象的基本流程是:entsel或ssget拿到对象名,entget取出对象信息表,修改表里的组码值,再用entmod写回。组码是 AutoCAD 的“黑匣子”,0 是对象类型,10 是起点/插入点,40 是半径/高度,8 是图层名。用assoc和cdr就能读出来:

(setq en (car (entsel "\n选择圆: "))) ; entsel 返回 (对象名 . 选点坐标) (setq ed (entget en)) ; 对象信息表 (setq r (cdr (assoc 40 ed))) ; 半径 (setq layer (cdr (assoc 8 ed))) ; 图层名

ssget批量选择时可以用过滤列表,这是选择集最强大的地方。下面的代码选出当前空间所有半径为 50 的圆:

(setq ss (ssget '((0 . "CIRCLE") (40 . 50.0)))) (setq n (sslength ss)) ; 选择集中对象数量 (setq i 0) (repeat n (setq en (ssname ss i)) ; 按索引取对象名 (setq ed (entget en)) ; 这里对每个圆做处理 (setq i (1+ i)) )

ssget的过滤列表是关键。(0 . "CIRCLE")按对象类型过滤,(40 . 50.0)按半径组码过滤。注意组码值必须和图中存储的类型一致,半径是实数就写50.0,写成50可能过滤不出来。选择集下落不明的坑在下一章专门讲。

entmod修改对象信息表后,屏幕上不一定立刻刷新。一般图元entmod后会自动更新,但复杂对象或插入块里的子对象,要补一句entupd才能重绘。这是“改了没反应”的常见原因。

4.3 示例:批量修改文字高度

把前面这些函数串起来,写一个实用工具:选中一批文字,统一把字高改成 3.5。整套逻辑覆盖选择集遍历、组码读取、组码修改、对象更新:

(defun c:TH (/ ss i en ed old new) (setq ss (ssget '((0 . "TEXT")))) ; 只选 TEXT 对象 (if ss (progn (setq i 0) (repeat (sslength ss) (setq en (ssname ss i)) (setq ed (entget en)) (setq old (cdr (assoc 40 ed))) ; 40 组码是文字高度 (setq new 3.5) (setq ed (subst (cons 40 new) (assoc 40 ed) ed)) ; 替换高度 (entmod ed) ; 写回图形 (entupd en) ; 强制刷新 (setq i (1+ i)) ) ) ) (princ) )

这里subst替换组码时,要同时传入新点和旧点:(subst (cons 40 new) (assoc 40 ed) ed),意思是“把表里第一个出现的旧组码换成新组码”。progn把多条表达式包成一组,因为if只能跟一个表达式。(princ)放在函数末尾是为了静默退出,不然函数返回最后一个表达式的值,命令区会多一行数字或列表。这个小习惯能让工具看起来更专业。

5. 常见问题排查与函数编写:五个高频踩坑场景的现场复盘

写 AutoLISP 最难受的不是语法难,而是“明明照着手册写了,就是不干活”。这一章复盘五个高频翻车现场,每条都按“现象 → 原因 → 解决”的顺序写,都是实际项目里会碰到的真问题。

5.1 加载即报错:括号不配平与中文符号

现象:(load "tool.lsp")后命令行提示malformed list on input,或者“无效的选项关键字”,文件一上来就挂了。原因:最常见是括号不配平,特别是长函数多处if嵌套时,少写一个右括号是常态;其次是从网页复制的代码混进了全角括号、全角引号,LISP 解析器不认。解决:编辑器里用括号匹配功能,把光标放在左括号上,看有没有对应的右括号高亮;文件里搜一下有没有中文括号和中文引号。我的习惯是每一层嵌套都缩进对齐,函数末尾的括号单独占一行,检查时一眼能数清层数。

5.2 返回值 nil:函数没输出,到底哪里出了问题

现象:调用自定义函数后,命令区只有nil,什么都没发生。原因:nil不一定是错误。while循环结束时返回 nil,princ返回 nil,setq的返回值是被赋的值而不是 nil,但如果函数最后一个表达式是(princ),整个函数就返回 nil,这是正常的静默退出。真正的问题是:if 条件判断错了分支,或者函数里某个setq赋值给了未被正确引用的局部变量。解决:在函数体里临时加(princ ...)打印中间变量,看是哪个环节断了;检查if的条件,AutoLISP 里 0 和空字符串都是“真”,只有nil是“假”,用(if (setq x ...))时要确认 x 确实被赋了非 nil 值。

5.3 坐标点不对:UCS 与 WCS 的转换

现象:用getpoint取的点画出来的线,位置明显不对,或者跟对象信息表里记录的坐标对不上。原因:getpoint返回的是当前 UCS 下的坐标,而entget得到的图元几何数据在 WCS 或 OCS 里。坐标系不一致,计算就全错。解决:统一用trans做转换。(trans pt 1 0)把点从 UCS 转到 WCS,(trans pt 0 1)转回来:

(setq pt (getpoint "\n指定点: ")) (setq pt_wcs (trans pt 1 0)) ; UCS 坐标转 WCS (setq pt_ucs (trans pt_wcs 0 1)) ; WCS 坐标转回 UCS

trans的第三个参数如果是整数 0、1、2,分别对应 WCS、UCS、显示坐标系。很多“点没画对”的玄学,最后都出在这。我的习惯是:从getpoint拿到的点,立刻转成 WCS 再参与计算,避免后面跟图元数据比较时空对空。

5.4 entmod 不生效:组码值类型与刷新顺序

现象:entmod执行了,没有任何报错,但图上的对象就是没变。原因有两类。第一类,组码值类型不对,比如半径组码 40 存的是实数,你给个整数 50,有的版本能接受,有的版本直接忽略。第二类,修改的是块表记录或复杂对象,entmod之后需要regen或entupd才能看到效果。解决:修改前先(cdr (assoc 40 ed))看数据类型,修改后用(entupd en)刷新对象,如果还没反应就(command "regen")。另外,entmod只能修改图元数据,不能新增对象,新增要entmake,这两个经常混。

5.5 加载 ARX 或 LSP 失败:版本、路径与依赖

现象:(arxload "some.arx")报“无法加载”,或(load "tool.lsp")提示找不到文件,但文件明明在。原因:ARX 程序跟 AutoCAD 版本强绑定,一个版本编译的 ARX 换到另一个版本就加载不了;LSP 文件路径里有中文或空格,某些旧版本处理不了;还有一类是 LSP 文件开头写了中文编码注释,加载时编码不兼容。解决:先用(findfile "some.arx")确认路径,再用arxload;路径尽量全英文;LSP 文件用 ANSI 编码保存,或者干脆不用中文注释,改用英文注释。load的第二个参数可以写加载失败时的提示字符串,比如(load "tool.lsp" "加载失败,检查文件路径"),这样至少知道失败原因而不是闷头报错。

6. 把手册变成工具箱:用 defun 组织自己的 LISP 函数库

手册翻完,最后要落在一个习惯上:按类别拆分函数,用defun封装成可复用的工具,而不是把几百个函数塞进一个文件。

我会按功能建若干文件:math.lsp放坐标转换、角度处理;list.lsp放列表工具函数;io.lsp放文件读写;geom.lsp放几何计算。每个文件里只放defun定义的函数,不写执行语句,主程序里用(load "geom.lsp")按需加载。这样做的好处是,一个函数出问题,只影响一个文件,排查范围小很多。

写命令类函数时,我强制自己套一个固定模板,这个模板能挡住一半以上的坑:

(defun c:TT (/ old p1 p2) (setq old (getvar "cmdecho")) ; 保存系统变量 (setvar "cmdecho" 0) ; 关闭命令回显 (setq p1 (getpoint "\n第一点: ")) (setq p2 (getpoint "\n第二点: ")) (command "line" p1 p2 "") ; 调 AutoCAD 命令画线 (setvar "cmdecho" old) ; 恢复系统变量 (princ) ; 静默退出 )

模板的三个要点:函数名后的/后面声明所有局部变量,避免污染全局;getvar保存被修改的系统变量,结束时恢复,不然用户的cmdecho设置被改掉,后面的操作会变得很吵;结尾的(princ)保证函数不返回多余值。如果是更复杂的命令,还要在*error*里也恢复系统变量,防止程序中断后变量停留在被修改状态。

从那以后我每次写工具函数,都强制走一遍“声明局部变量、保存系统变量、恢复、静默退出”这个流程,出问题的概率低了很多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询