资讯动态

AutoLISP函数手册实战:从数学运算到选择集操作的核心技巧

发布时间:2026/10/24 16:53:35 来源:尧图企业网站定制
简介《LISP函数大全分类手册》是一份面向AutoLISP初学者与CAD二次开发者的函数速查资料以PDF形式系统梳理了AutoLISP常用函数帮助读者在编写和调试LISP程序时快速定位语法与用途。资源包内仅含1个PDF文件大小约47KB轻量便携适合随时查阅。手册按功能划分为数学运算、检验与逻辑运算、转换运算、列表处理等模块逐条列出函数原型与返回值说明例如数学运算中的加减乘除、绝对值、三角函数与最大公约数列表处理中的car、cadr、cons、mapcar、nth等并延伸至字符串、文件、等待输入及几何运算等常用函数。内容预览显示其条目清晰、示例直接便于对照记忆。目前已有765人学习下载适合需要系统掌握AutoLISP函数体系、提升CAD编程效率的开发者作为案头参考。1. 从一份函数清单说起AutoLISP 手册到底能解决什么问题如果你在 CAD 二次开发这条路上待过一阵大概率遇到过这种场景想写个批量改图层的小工具脑子里知道大概要遍历选择集、改对象属性但具体用哪个函数、参数顺序是什么、返回值是列表还是点对全得靠翻旧笔记或者去搜零散帖子。手边那份《LISP函数大全分类手册》就是干这个用的——它不是教程是一份按功能域归类的 AutoLISP 函数速查表覆盖数学运算、逻辑判断、类型转换、列表处理、字符串与文件、交互输入、几何运算、对象与选择集操作、符号表、系统变量控制、流程控制、错误处理、显示打印、内存管理一直到 ADS/ARX 外部函数和 3D 渲染相关调用粗算下来十九个大类、三百多个函数条目。这份手册适合两类人一类是刚接触 AutoLISP、需要一份能按功能查的索引来建立函数地图的新手另一类是有一定经验、但记不全冷门函数比如boole的位运算参数、trans的坐标系统转换、nentsel返回的嵌套实体信息结构的老手。它不教你编程思想但能让你在写代码时少翻几次文档。下面我按「怎么把这份清单用起来」的思路拆几个关键模块的实操方式和踩坑点。2. 数学与逻辑函数别被「简单」两个字骗了2.1 数学运算函数的参数陷阱手册里数学部分列了二十来个函数看起来最没技术含量但实际写几何计算时翻车最多的恰恰是这里。先看基础四则和取整;; 基础数学运算示例 (setq a 10.0) (setq b 3) ;; 除法AutoLISP 的 / 是实数除法不是整数截断 (/ a b) ; 返回 3.33333 (/ 10 3) ; 返回 3两个整数相除结果为整数 (/ 10.0 3) ; 返回 3.33333 ;; fix 截断取整不是四舍五入 (fix 3.9) ; 返回 3 (fix -3.9) ; 返回 -3 ;; rem 取余符号跟随被除数 (rem 10 3) ; 返回 1 (rem -10 3) ; 返回 -1 ;; gcd 最大公约数 (gcd 48 36) ; 返回 12这里第一个坑就是整数除法和实数除法的区别。(/ 10 3)返回 3 而不是 3.333因为两个参数都是整数AutoLISP 会做整数除法。很多人在算比例、算间距的时候忘了这一点结果精度直接丢掉。解决办法很简单至少让一个操作数是实数写成(/ 10.0 3)或者(/ 10 3.0)。第二个坑是fix的行为。它不是四舍五入是直接截断小数部分。(fix 3.9)得 3(fix -3.9)得 -3。如果你需要四舍五入得自己写(fix ( x 0.5))但注意负数情况下这个公式会出问题负数要写成(fix (- x 0.5))。第三个坑是三角函数的角度单位。手册里写得很清楚sin、cos、atan的参数和返回值都是弧度不是角度。但实际画图时用户输入的角度往往是度数所以中间必须做转换;; 角度转弧度 (defun dtr (deg) (* deg (/ pi 180.0)) ) ;; 弧度转角度 (defun rtd (rad) (* rad (/ 180.0 pi)) ) ;; 使用示例求 30 度的正弦值 (sin (dtr 30)) ; 返回 0.5dtr和rtd这两个转换函数几乎是每个 AutoLISP 项目的标配建议直接放进你的工具函数库里。pi是内置常数不需要自己定义直接用就行。2.2 逻辑比较函数的返回值语义逻辑函数这块手册列了、/、、、、、and、or、not、eq、equal等。看起来都是比较但返回值语义有细微差别用错了会导致条件判断走错分支。;; 数值比较 ( 1 1) ; 返回 T ( 1 1.0) ; 返回 T数值相等类型不同也返回 T ( abc abc) ; 返回 T字符串比较 ( abc ABC) ; 返回 nil字符串比较区分大小写 ;; eq 和 equal 的区别 (setq list1 (1 2 3)) (setq list2 (1 2 3)) (eq list1 list2) ; 返回 nileq 比较的是同一对象引用 (equal list1 list2) ; 返回 Tequal 比较的是内容 ;; equal 可以带差量参数用于浮点数比较 (equal 1.0 1.0001) ; 返回 nil (equal 1.0 1.0001 0.001) ; 返回 T差量在 0.001 以内视为相等eq和equal的区别是新手最容易混淆的地方。eq比较的是两个表达式是否指向同一个对象引用相等equal比较的是内容是否相同值相等。对于列表和字符串几乎总是应该用equal。浮点数比较更是必须用equal带差量参数因为浮点运算的精度误差会让返回意料之外的结果。and和or的短路特性也值得注意。(and a b c)会从左到右求值遇到第一个 nil 就停止并返回 nil(or a b c)遇到第一个非 nil 就停止并返回那个值。这意味着你可以把开销大的判断放在后面利用短路来优化性能。3. 列表处理与类型转换AutoLISP 的核心操作3.1 列表处理函数的组合用法AutoLISP 的名字里就带着 List列表处理是这门语言最核心的部分。手册第四章列了car、cadr、caddr、cdr、cons、list、append、length、nth、member、assoc、subst、reverse、mapcar、foreach这些函数。单独看每个都不难但组合起来能做的事情很多。;; 构建和拆解列表 (setq pts (list (0 0) (10 0) (10 10) (0 10))) ;; car 取第一个元素cdr 取剩余部分 (car pts) ; 返回 (0 0) (cdr pts) ; 返回 ((10 0) (10 10) (0 10)) ;; cadr 等价于 (car (cdr ...))取第二个元素 (cadr pts) ; 返回 (10 0) ;; caddr 取第三个元素 (caddr pts) ; 返回 (10 10) ;; nth 按索引取元素索引从 0 开始 (nth 2 pts) ; 返回 (10 10) ;; length 取列表长度 (length pts) ; 返回 4 ;; append 合并列表 (append (1 2) (3 4)) ; 返回 (1 2 3 4) ;; cons 将元素添加到列表头部 (cons (5 5) pts) ; 返回 ((5 5) (0 0) (10 0) (10 10) (0 10)) ;; reverse 反转列表 (reverse pts) ; 返回 ((0 10) (10 10) (10 0) (0 0))这里有个容易踩的坑append和cons的区别。append把两个列表拼接成一个新列表cons把单个元素加到列表头部。如果你用(cons (1 2) (3 4))得到的是((1 2) 3 4)而不是(1 2 3 4)。想要拼接效果必须用append。另一个坑是nth的索引从 0 开始不是从 1 开始。写循环的时候如果忘了这一点会漏掉第一个元素或者越界。mapcar是列表处理里最强大的函数之一它把某个函数应用到多个列表的对应元素上返回结果列表;; 对列表中每个元素加 1 (mapcar 1 (1 2 3 4)) ; 返回 (2 3 4 5) ;; 两个列表对应元素相加 (mapcar (1 2 3) (10 20 30)) ; 返回 (11 22 33) ;; 提取点列表中的所有 X 坐标 (setq xs (mapcar car pts)) ; 返回 (0 10 10 0) ;; 提取所有 Y 坐标 (setq ys (mapcar cadr pts)) ; 返回 (0 0 10 10)用mapcar配合car/cadr来批量提取坐标是处理点列表的标准操作。比手写foreach循环简洁得多而且不容易出错。3.2 类型转换函数的精度与格式控制手册第三章的转换函数在实际项目中出场率极高尤其是rtos、atof、itoa、angtos这几个。CAD 开发中经常需要在数值和字符串之间来回转换——比如把计算结果写到图纸标注里或者从用户输入的字符串里解析出数值。;; 实数转字符串控制小数位数 (rtos 3.1415926 2 2) ; 返回 3.14模式 2 为十进制精度 2 位小数 (rtos 3.1415926 2 4) ; 返回 3.1416 (rtos 100.0 2 0) ; 返回 100 ;; 字符串转实数 (atof 3.14) ; 返回 3.14 (atof abc) ; 返回 0.0无法解析时返回 0 ;; 字符串转整数 (atoi 42) ; 返回 42 (atoi 3.14) ; 返回 3截断小数部分 ;; 整数转字符串 (itoa 42) ; 返回 42 ;; 角度转字符串 (angtos 1.5708 0 2) ; 返回 90.00模式 0 为十进制度数 (angtos 1.5708 1 4) ; 返回 90d00\模式 1 为度分秒rtos的模式参数决定了输出格式模式 1 是科学计数法模式 2 是十进制模式 3 是工程格式模式 4 是建筑格式。精度参数控制小数位数。这两个参数配合使用可以精确控制输出字符串的样子。atof和atoi在解析失败时不会报错而是返回 0。这个行为有时候是便利有时候是隐患。如果你需要严格校验用户输入不能只靠atof的返回值来判断得先用distof或者自己写正则检查。trans函数是坐标转换的关键手册里只给了一行说明但实际用起来参数含义需要仔细理解;; 将用户坐标系中的点转换到世界坐标系 (trans (10 20 0) 1 0) ; 从 UCS 转到 WCS ;; 将世界坐标系中的点转换到当前用户坐标系 (trans (10 20 0) 0 1) ; 从 WCS 转到 UCS ;; 将点从当前坐标系转到指定视口坐标系 (trans pt 1 2) ; 从 UCS 转到 DCS显示坐标系trans的第二个参数是原坐标系代码第三个参数是新坐标系代码。常用的代码有0 表示 WCS世界坐标系1 表示当前 UCS用户坐标系2 表示 DCS显示坐标系。写涉及坐标拾取和转换的代码时如果不做trans转换拿到的点可能跟你预期的不一样尤其是在用户旋转了 UCS 之后。4. 对象操作与选择集从「拿到对象」到「改对属性」4.1 选择集构建的过滤技巧手册第九章的选择集函数是 CAD 批量操作的基础。ssget是最核心的一个它的过滤列表参数决定了你能多精确地筛选目标对象。;; 选取所有实体 (setq ss (ssget X)) ;; 选取所有圆 (setq ss (ssget X ((0 . CIRCLE)))) ;; 选取图层 WALL 上的所有直线 (setq ss (ssget X ((0 . LINE) (8 . WALL)))) ;; 选取半径大于 50 的圆 (setq ss (ssget X ((0 . CIRCLE) (-4 . ) (40 . 50.0)))) ;; 选取颜色为红色的对象颜色号 1 (setq ss (ssget X ((62 . 1)))) ;; 组合条件图层为 WALL 或 DOOR 上的直线 (setq ss (ssget X ((0 . LINE) (-4 . OR) (8 . WALL) (8 . DOOR) (-4 . ENDOR))))过滤列表的格式是点对(组码 . 值)。组码 0 是对象类型8 是图层名62 是颜色号40 是圆的半径。逻辑运算符用-4组码配合字符串表示OR、AND、NOT、、、等。这些运算符可以嵌套但要注意配对闭合。一个常见的坑是ssget的过滤条件对不同类型的对象行为不一致。比如组码 40 对圆是半径对文字是字高对多段线是全局宽度。如果你写了一个过滤条件但没指定对象类型可能会选到意料之外的东西。所以过滤列表里最好总是带上(0 . 类型)。遍历选择集的标准写法;; 遍历选择集统计对象数量 (setq ss (ssget X ((0 . LINE)))) (setq count (sslength ss)) (setq i 0) (while ( i count) (setq ent (ssname ss i)) (setq edata (entget ent)) ;; 在这里处理每个对象 (setq i (1 i)) )sslength返回选择集中的对象数量ssname按索引取出对象名索引从 0 开始entget取出对象的属性列表。这个三段式循环是 AutoLISP 批量处理的标准骨架几乎每个工具都会用到。4.2 实体属性修改的正确姿势拿到实体数据列表之后修改属性的流程是用entget取出数据用subst替换要改的组码值用entmod写回最后用entupd刷新显示。;; 修改选中直线的图层 (defun c:chglayer ( / ent edata newdata) (setq ent (car (entsel \n选择要修改的直线: ))) (setq edata (entget ent)) ;; 替换组码 8图层名的值 (setq newdata (subst (cons 8 NEWLAYER) (assoc 8 edata) edata)) ;; 写回修改后的数据 (entmod newdata) ;; 刷新实体显示 (entupd ent) (princ \n图层已修改。) (princ) )这段代码里subst的用法是关键(subst 新项 旧项 列表)会在列表中查找旧项并替换为新项。assoc用来找到组码对应的点对。顺序不能反subst的第一个参数是新值第二个是旧值。entmod修改数据后屏幕上的图形不一定会立即刷新尤其是修改了顶点坐标或者文字内容的情况。entupd强制刷新指定实体的显示。如果修改的是块定义内部的实体还需要用entupd传入块名来刷新整个块。另一个坑是entmod不能修改某些只读属性比如实体的句柄组码 5和某些扩展数据。尝试修改这些属性会静默失败不报错但也不生效。遇到修改不生效的情况先检查是不是动了不该动的组码。5. 避坑与排查那些手册不会告诉你的翻车现场5.1 变量未声明导致的「玄学」行为AutoLISP 的变量默认是全局的除非你在defun的变量列表里用/分隔声明为局部变量。不声明局部变量的后果是函数执行完后变量还留在内存里下次调用时可能带着上次的残留值导致结果时对时错。;; 错误写法i 和 ent 是全局变量 (defun c:badfunc () (setq i 0) (while ( i 10) (setq ent (ssname ss i)) (setq i (1 i)) ) ) ;; 正确写法i 和 ent 声明为局部变量 (defun c:goodfunc ( / i ent) (setq i 0) (while ( i 10) (setq ent (ssname ss i)) (setq i (1 i)) ) )现象是函数第一次运行正常第二次运行结果不对。原因是第一次运行后i的值留在了内存里第二次运行没有重新初始化。解决办法就是在defun的变量列表里用/把局部变量列出来。这个坑几乎每个 AutoLISP 新手都会踩一次而且因为现象不稳定排查起来很费时间。5.2 选择集为空时的静默失败ssget在没有选中任何对象时返回nil但后续的sslength和ssname对nil操作不会报错而是返回 0 或者nil。这导致代码看起来在正常运行实际上什么都没做。;; 有问题的写法没有检查 ssget 返回值 (setq ss (ssget X ((0 . CIRCLE)))) (setq n (sslength ss)) ; ss 为 nil 时返回 0 (princ (strcat \n找到 (itoa n) 个圆。)) ;; 稳妥的写法先判断选择集是否有效 (setq ss (ssget X ((0 . CIRCLE)))) (if ss (progn (setq n (sslength ss)) (princ (strcat \n找到 (itoa n) 个圆。)) ) (princ \n没有找到符合条件的圆。) )现象是程序不报错但也不产生任何效果。原因就是选择集为空后续循环一次都没执行。解决办法是在ssget之后立即用if判断返回值为空时给出提示或者提前返回。5.3 坐标转换遗漏导致的偏移在用户自定义坐标系下拾取的点如果不做trans转换就直接用于entmake或者command调用画出来的东西会偏离预期位置。;; 有问题的写法直接使用 getpoint 的返回值 (defun c:badcircle ( / pt) (setq pt (getpoint \n指定圆心: )) (command CIRCLE pt 50) ; UCS 旋转时位置会偏 ) ;; 正确的写法转换到 WCS 再使用 (defun c:goodcircle ( / pt) (setq pt (getpoint \n指定圆心: )) (setq pt (trans pt 1 0)) ; 从 UCS 转到 WCS (command CIRCLE pt 50) )现象是在默认 WCS 下运行正常用户一旦旋转了 UCS画出来的圆就跑到别的地方去了。原因是getpoint返回的是当前 UCS 下的坐标而command调用的CIRCLE命令在某些情况下期望的是 WCS 坐标。解决办法是在拾取点之后、使用之前统一用(trans pt 1 0)转到 WCS。5.4 字符串比较的大小写陷阱用于字符串比较时区分大小写但用户输入往往大小写不固定。用直接比较用户输入和预设字符串很容易因为大小写不一致而判断失败。;; 有问题的写法区分大小写 (if ( (getstring \n输入选项(Y/N): ) Y) (princ \n你选择了是。) (princ \n你选择了否。) ) ;; 稳妥的写法统一转大写再比较 (if ( (strcase (getstring \n输入选项(Y/N): )) Y) (princ \n你选择了是。) (princ \n你选择了否。) )现象是用户明明输入了 Y程序却走了否的分支。原因是用户输入的是小写 y而比较时 y 不等于 Y。解决办法是用strcase把输入统一转成大写或小写再比较。strcase的第二个参数传T转小写不传或传nil转大写。5.5 文件句柄未关闭导致的锁定用open打开文件读写后如果中间发生错误导致close没执行文件会一直被锁定后续操作无法访问。;; 有风险的写法出错时文件不会关闭 (setq f (open data.txt w)) (write-line some data f) (close f) ;; 稳妥的写法用错误处理确保文件关闭 (defun c:writefile ( / f) (setq f (open data.txt w)) (if f (progn (write-line some data f) (close f) ) (princ \n无法打开文件。) ) )现象是程序运行一次后再次运行时报错说文件无法访问。原因是上一次运行中文件句柄没有正常关闭。解决办法是在打开文件后立即判断句柄是否有效并在所有可能的退出路径上都执行close。更严谨的做法是用*error*自定义错误处理函数在出错时关闭文件。6. 进阶技巧用 mapcar 和 apply 写出更紧凑的代码6.1 mapcar 配合 lambda 实现匿名函数mapcar的第一个参数可以是函数名也可以是一个lambda表达式。用lambda可以避免为了一次性操作单独定义命名函数。;; 用 lambda 对列表每个元素做平方 (mapcar (lambda (x) (* x x)) (1 2 3 4 5)) ; 返回 (1 4 9 16 25) ;; 用 lambda 提取点列表中的 Z 坐标 (setq pts ((0 0 0) (10 0 5) (10 10 3))) (mapcar (lambda (p) (caddr p)) pts) ; 返回 (0 5 3) ;; 用 lambda 对两个列表做自定义运算 (mapcar (lambda (a b) ( (* a 2) b)) (1 2 3) (10 20 30)) ; 返回 (12 24 36)lambda的写法是(lambda (参数) 表达式)前面的单引号不能少否则 AutoLISP 会试图求值。这个技巧在需要对列表做一次性转换时特别有用代码量能减少一半。6.2 apply 将列表展开为参数apply把函数应用到参数列表上相当于把列表「展开」成函数的多个参数。;; 用 apply 求和 (apply (1 2 3 4 5)) ; 返回 15 ;; 用 apply 求最大值 (apply max (3 1 4 1 5 9)) ; 返回 9 ;; 用 apply 调用自定义函数 (defun my-add (a b c) ( a b c)) (apply my-add (10 20 30)) ; 返回 60apply和mapcar的区别mapcar对列表的每个元素分别应用函数返回结果列表apply把整个列表作为参数传给函数返回单个结果。当你有一个参数列表需要传给一个接受多个参数的函数时apply就是正确的工具。6.3 用 assoc 和 subst 做数据驱动的属性修改结合assoc、subst和mapcar可以写出很紧凑的批量属性修改代码;; 批量修改选择集中所有对象的颜色 (defun c:batchcolor ( / ss i ent edata) (setq ss (ssget X ((0 . LINE,CIRCLE,ARC)))) (if ss (progn (setq i 0) (while ( i (sslength ss)) (setq ent (ssname ss i)) (setq edata (entget ent)) ;; 如果已有颜色组码则替换没有则添加 (if (assoc 62 edata) (setq edata (subst (cons 62 1) (assoc 62 edata) edata)) (setq edata (append edata (list (cons 62 1)))) ) (entmod edata) (setq i (1 i)) ) (princ (strcat \n已修改 (itoa (sslength ss)) 个对象的颜色。)) ) (princ \n没有找到符合条件的对象。) ) (princ) )这段代码里有个细节值得注意如果实体本身没有颜色属性组码 62assoc返回nil此时不能用subst替换而应该用append把新的点对追加到数据列表末尾。这个判断分支是很多批量修改工具容易忽略的地方——对于「默认颜色」的实体直接subst会失败因为找不到要替换的旧项。从那以后我每次写批量修改属性的代码都会先检查目标组码是否存在存在就subst不存在就append这个习惯帮我省了不少调试时间。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑