在 AutoLISP(基于 XLISP 的方言)中,定义函数主要通过 defun 关键字实现。根据函数的作用域、参数传递方式及返回值特性,可分为标准命名函数、局部变量函数、匿名函数(Lambda)及命令函数四种核心模式。
1. 函数定义方法分类表
| 定义类型 | 语法结构特征 | 作用域/可见性 | 典型应用场景 |
|---|---|---|---|
| 标准全局函数 | (defun 函数名 (参数列表) 表达式...) |
全局可见,可在任何位置调用 | 通用工具库、数学计算逻辑 |
| 带局部变量函数 | (defun 函数名 (参数 / 局部变量) 表达式...) |
全局可见,但局部变量仅在函数内有效 | 复杂逻辑处理,避免污染全局符号表 |
| 命令函数 | (defun c:命令名 ( / 局部变量) 表达式...) |
注册为 AutoCAD 命令行指令 | 用户交互操作、绘图插件入口 |
| 匿名函数 (Lambda) | '(lambda (参数) 表达式...) |
仅作为参数传递给高阶函数(如 mapcar) |
临时数据处理、列表映射操作 |
2. 代码实现与详解
2.1 标准全局函数与局部变量
这是最基础的定义方式。斜杠 / 是分隔符,其左侧为形式参数(传入值),右侧为局部变量(内部临时存储)。AutoLISP 强烈建议使用局部变量以防止全局环境污染 。
lisp
;;; 示例:计算矩形面积
;;; 语法:(defun 函数名 (形参 / 局部变量) 函数体)
(defun calc-rect-area (width height / area) ;; 计算逻辑
(setq area (* width height))
;; 默认返回最后一个表达式的值
area
)
;; 调用示例
;; (setq result (calc-rect-area 10.0 5.0)) -> 返回 50.0
2.2 命令函数(Command Function)
在 AutoCAD 环境中,若希望函数能直接在命令行通过名称执行,需在函数名前添加 c: 前缀。此类函数通常不接收参数,而是通过 getpoint、getdist 等交互函数获取数据 。
lisp
;;; 示例:绘制一个固定半径的圆
;;; 注意:c: 前缀使其成为 CAD 命令 "DRAWFIXEDCIRCLE"
(defun c:DRAWFIXEDCIRCLE ( / center-pt radius)
(princ "
=== 绘制固定半径圆 ===")
;; 获取用户输入的中心点
(setq center-pt (getpoint "
指定圆心位置:"))
(if center-pt
(progn
;; 设置局部变量半径
(setq radius 50.0)
(princ (strcat "
正在绘制半径为 " (rtos radius) " 的圆..."))
;; 调用 CAD 原生命令
(command "_.CIRCLE" center-pt radius)
(princ "
绘制完成。")
)
(princ "
未指定圆心,操作取消。")
)
(princ) ; 静默退出,不返回 nil 到命令行
)
2.3 匿名函数 (Lambda Expressions)
Lisp 支持将函数作为数据传递。lambda 定义的函数没有名字,通常配合 mapcar、apply 或 vl-sort 等高阶函数使用,用于临时逻辑处理 。
lisp
;;; 示例:将列表中的每个元素平方
;;; 使用 lambda 定义临时匿名函数
(setq original-list '(1 2 3 4 5))
;; mapcar 第一个参数接受一个函数(此处为 lambda 匿名函数)
(setq squared-list
(mapcar '(lambda (x) (* x x)) original-list)
);; 结果:squared-list -> (1 4 9 16 25)
;;; 示例:自定义排序逻辑(按距离原点远近排序点列表)
(setq pt-list '((34) (1 1) (5 12)))
(setq sortedpts
(vl-sort pt-list
'(lambda (p1 p2)
(< (distance '(0 0) p1) (distance '(0 0) p2))
)
)
)
;; 结果:sorted-pts -> ((1 1) (3 4) (5 12))
3. 核心机制解析
- 返回值机制 :AutoLISP 函数默认返回函数体中最后一个被求值的表达式的结果 。若需提前返回或返回特定值,需确保该值位于末尾,或使用
progn包裹多个表达式并将目标值置于最后 。 - 参数传递 :AutoLISP 采用传值调用 (Call by Value)。对于原子(数字、字符串),修改形参不影响实参;对于表(List),虽然传递的是引用副本,但在函数内直接修改表结构需谨慎,通常建议通过
setq重新绑定局部变量 。 - 递归支持:Lisp 天然支持递归定义,即函数在自身定义体内调用自身。这在处理树状结构(如选择集遍历、图元数据嵌套)时非常高效,但需注意设置终止条件以防死循环 。
- 宏系统扩展 :虽然基础
defun用于定义运行时函数,但 Common Lisp 及部分高级 LISP 环境支持defmacro定义宏。宏在代码编译/加载阶段进行文本替换和逻辑展开,允许程序员创建新的语法结构,这是 LISP"代码即数据"特性的最高级体现 。但在标准 AutoLISP中,主要依赖defun和函数组合来实现逻辑复用。
4. 最佳实践建议
- 强制局部化 :始终在
defun的参数列表中使用/声明所有中间变量为局部变量,避免意外覆盖同名的全局变量(如pt、ss等常用名)。 - 命令命名规范 :自定义命令建议加前缀(如
MY:或特定缩写),避免与 AutoCAD 原生命令或其他插件冲突。 - 错误处理 :在复杂函数中结合
*error*全局变量定义错误处理函数,确保在用户取消操作(Esc)或发生错误时能清理临时对象(如未完成的图元)。