当前位置:文档之家› AutoLISP 基础——认识自定义函数

AutoLISP 基础——认识自定义函数


例 4-2 (defun absv(x) ;定义一个绝对值函数
(if (>= x 0)
x ;单一变量表达式
(- x)
)
)
⑤ 如果我们需要定义一个匿名函数,则可以使用 lambda 函数,除
函数名"缺失" 外,其它都和 defun 相同。匿名函数在使用时定
义,普通的函数则可以在使用前,或任何你觉得需要的时候定
((lambda(x y z) (* x y z)) 3 4 5)是合理的。 ③ 参数可以是:数字(整数、实数)、字符串、T、nil、表(如果此
表是表、函数,就构成了复杂表,这就是lisp程序)、函数。 ④ 表可以作为函数的参数。如(+ 1 (- 5 2) 3)中,表(- 5 2)就作为+函数
的参数。 ⑤ 表的返回值也可以传递给其它变量,如(setq x (+ 1 2 3)) ⑥ 接受输入参数,如(- 5 2)中,-函数接受5和2两个参数。
是表。每个表都返回一个可由外层表达式使用的值。如果没有外
层表达式,AutoLISP 将则该值返回给AutoCAD 命令行。例如,
如下代码调用了三个函数:
(fun1 (fun2 arguments)(fun3 arguments))
;式 1-1
第一个函数 fun1 有两个参数,另两个函数 fun2 和 fun3 分别作为
2. 匿名函数 在一下表达循环的函数(比如 mapcar、forearch 等)的使用中,我 们往往见到使用 lambda 定义的匿名函数,这些函数内容我们也可以作 为一个"模块"定义为一个有名函数,然后对函数名进行引用,不过那样 会占用更多的资源,相对而言也就不够效率了。
七、 函数的作用机理
函数在不同情况下使用,其作用机理也是不同的。根据其和"外界"的 交互作用的不同,笔者将函数的作用机理归结为"返回值作用"和"过程作 用" 。
函数同为数据对象,就意味着我们可以像对待其他对象那样把它传递 给其他函数。这种性质对于 AutoLISP 这种自底向上程序设计至关重要。 AutoLISP 函数语法的描述遵循如下
这是 AutoLISP 中系统自带函数的一般性语法,也包括用其它语言定 义的函数。
所有 AutoLISP 表达式的格式都如下所示:
上式一对英文括号组成的表达式,称之为表,Lisp语言也称为表的语 言。表有两种形式,一种是“口袋式”表;一种是“函数式”表。前者 如’(0 0)表示一个2维点,前面加’表示此表不求值。如果不加‘,则通 常是认为是“函数式”表。“函数式”表如下:
① 结构特点:左括号(紧跟函数,函数所需要的参数,右括号)结束。 ② 函数是指:系统定义的函数、自定义的函数、匿名函数lambda。如
有重复使用代码的地方,定义函数是必要的,通常可以提高运行速 度。
1. 作为"模块"的自定义函数 程序应该有良好的结构,不仅仅是便于阅读,同时也对程序的运行 效率有一定影响。多数的高级语言通过模块和转移(goto)来完成程序 的结构化,和这些高级语言 不同的一点,AutoLISP 可以通过自定义函 数来完成这些要求,不需要编译就可以直接使用的代码,相比其他语言 来说,更具灵活性。作为"模块"使用的典型的自定义函数主要有两类, 一类是为对话框控件定义的动作函数,如: 例 6-1
综合以上描述,我们可以看到"函数"的基本组成: 输入值:一般称为参数; 表达式:函数体,是代码的集合,共同组成上面所说的"法则";
输出值:表达式对应输入值的(唯一)结果,通常称为"返回值 ";
封装:不同的语言,封装的意义也不同,在开放式的 AutoLISP 语言中, "封 装"并不意味着编译或打包,只使之以完整的函数体 驻留在内存中也应可以称为"封装" ;
二、 什么是函数 函数一词最早来源于数学,维基百科这样说:函数是将唯一的输出
值赋予每一输入的"法则"以及该输出值与对应输入值的集合。 在程序设计语言中,可以将一段经常需要使用的代码封装起 来,在需要使用时可以直接调用,所以,函数也可以说是许多代码的集 合,这就是程序中的函数。
从以上意义上,笔者建议使用"程序名(或部分字母)+特殊字符+ 函数名"这种命名方式,如 ca_main、tr:trans等。
虽然没有明确限制, 但一些前缀在 AutoCAD 中有自己的意义。比 如"ai_"AutoCAD(随系统附带的扩展函数),"acet_" AutoCAD ExpressTools 附带的扩展函数)等,另外一些著名的第三方 Lisp 扩展 库,都会有自己的"专属"前缀,使用时也都应该注意避免。
以上所说的是一般意义上的函数,也可以理解为有名函数,在 AutoLISP 中还有一种可以理解为"临时"定义的一次性的匿名函数 (lambda 表达式)即没有名字的函数,lambda 表达式基于数学上闭包 问题中ห้องสมุดไป่ตู้"演算"而得名,因为无名,所以无法重复调用,其它规则均不 违背。
六、 何时使用自定义函数
义。
⑥ 除此之外,给符号名赋值的 set 族函数也可以用来定义函数,这
个问题将在后面会阐述。
⑦ 只有函数接受参数输入,如(setq tt '(* x 3)),tt是一个表,不是函
数,故(tt 3)是错误的。(setq tt +)从此以后tt代表函数+,故(tt 1
2 3)是正确的。
⑧ 表可以作为参数传递给其它函数,换言之,函数每个参数都可以
细斟酌。 对于我们这些少量编程的 AutoCAD 的用户而言,过度复杂的函数
名也会给使用带来不便,所以,只要有限的标识,起到便于识别的目的 即可。
除非声明成局部变量,否则函数一经定义并加载,函数会驻留在 AutoCAD 的内存中,不同程序的同名函数会相互干扰,造成程序不可 用或结果错误,因此,在函数的命名时,也应考虑不同程序间函数的相 互避让。
这里所说的"符号"包括函数名、参数名和变量名。 需要说明的是,AutoLISP 函数名和变量名使用同一个命名空间(部 分其它的 LISP语言函数和变量使用不同的命名空间) 也就是说,变量 和函数重名时会冲突,后面定义(赋值)的会替代前面的。 可以看出,AutoLISP 的命名规则是很"宽松"的,除常见的字母数字 命名外,如"abc_1、+3+、xd::abc、<3"这类看似怪异的名字都是合法 的。 即便如此,也有些看似合法的名字是不能使用的,主要有: 系统的内部函数名,如 princ,car 等; 系统保留的常量名或其它符号,如 pi、t、nil、pause、^C 等; 系统有其它解释的符号组合,如 1e2(科学计数法) +3、-2(数 值)。 其它一些用于表达方向的字母(N、S、E、W)及表达角度的字母 (r、g、d)等 在某些时候也会产生意想不到的错误,所以,在使用的时候也应该 谨慎。因为 AutoLISP 对多字节字符的支持不是太好,所以,虽然理论 上可以使用汉字来作为函数名(或变量名),但实际上还是应该尽量避 免的,否则可能会出现无法预料的错误。 一些特殊的表达方式在 AutoLISP 中有特殊的含义,这些包括前缀c: 和函数名s::startup,前者表示定义了一个外部函数,而后在则定义了一个 自动执行函数。 我们用"c:3"来定义一个外部函数,使用命令"3"来执行,看起来这不 符合规则,似乎使用了数字"3"来作为函数名了,但其实不然,函数名是 "c:3" 。可以看出AutoLISP 中(或者说是 AutoCAD 中) 命令名是有独 立空间的。即便如此,我们也不建议这样用,一般说来,函数、变量, 尤其是命令的命名应 该能表达某种意义,向使用者和阅读者传递某种相 关的信息,而过度简单且不能引起联想的纯数字是无法承载这个任务 的。 从这个意义来说,函数名(或变量名)的命名每一个字节都应该仔
(function arguments)
;式 3-1
四、 函数的定义 一般说来,函数通过 defun 系列函数来定义,语法如下:
(defun sym argument-list expr ...) ;式 4-1
① defun 函数定义一个名为sym的函数(defun是系统定义的一个函 数,其功能就是用来用户定义自己的函数)。
② 函数名后面跟随参数列表argument-list,参数列表中对函数使用的 参数和局部变量进行声明,如果不申明任何参数和局部变量,必
须在函数名后提供一对空括号或者 nil (注:isp 语言中 nil 和一 对空括号是等价的)。不管是否需要声明参数,但"参数列表"项 是必须的,这是函数区别于赋值的一个重要特点。 ③ expr 为函数sym执行时的AutoLISP 表达式,可以是数字(整数、 实数)、字符串、T、nil、表、函数。见如下例 4-2 ④ AutoLISP 定义的函数本身是无法包括"可选参数"的,函数定义 时,参数数量一经声明,使用时就无法增减,否则就会出现"参数 太多(少)"的错误。例如: 例 4-1 (defun tan(x) ;定义一个正切函数,函数名 tan,有一个参数 x (/ (sin x) (cos x)) ;函数体 ) 使用这个函数时,必须而且只能输入一个变量,否则出错。而系统 自己定义的函数,参数的数量可是变的,如+。
可以经常调用:这是"函数"定义的一个基本要求,不能经常调用 就不必封装成函数了。
三、 AutoLISP 函数表达式 与很多的高级语言不同,函数也是AutoLISP(注:所有种类的Lisp
相关主题