AutoLISP 基础——认识自定义函数
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
② 函数名后面跟随参数列表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 基础——认识自定义函数
(本文由LL_J“认识自定义函数”和“Autolisp编程心得”两篇巨著合成, 并融入了其他人的一些经验,以快速打通你的任督二脉——自贡黄明儒注)
一、 初识Lisp 在AutoCad命令输入(+ 1 2 3),回车返回6,如下
命令: (+ 1 2 3) 6
恭喜你,你已经会写Lisp程序了。这里,我们用到了一个系统定义的 函数+,这个函数的作用就是对后面的数字求和。
二、 什么是函数 函数一词最早来源于数学,维基百科这样说:函数是将唯一的输出
值赋予每一输入的"法则"以及该输出值与对应输入值的集合。 在计算机领域,对函数并没有一个完整的定义,百度百科有这样一
句话:"许多程序设计语言中,可以将一段经常需要使用的代码封装起 来,在需要使用时可以直接调用,所以,函数也可以说是许多代码的集 合,这就是程序中的函数。
有重复使用代码的地方,定义函数是必要的,通常可以提高运行速 度。
1. 作为"模块"的自定义函数 程序应该有良好的结构,不仅仅是便于阅读,同时也对程序的运行 效率有一定影响。多数的高级语言通过模块和转移(goto)来完成程序 的结构化,和这些高级语言 不同的一点,AutoLISP 可以通过自定义函 数来完成这些要求,不需要编译就可以直接使用的代码,相比其他语言 来说,更具灵活性。作为"模块"使用的典型的自定义函数主要有两类, 一类是为对话框控件定义的动作函数,如: 例 6-1
((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两个参数。
例 4-2 (defun absv(x) ;定义一个绝对值函数
Fra Baidu bibliotek
(if (>= x 0)
x ;单一变量表达式
(- x)
)
)
⑤ 如果我们需要定义一个匿名函数,则可以使用 lambda 函数,除
函数名"缺失" 外,其它都和 defun 相同。匿名函数在使用时定
义,普通的函数则可以在使用前,或任何你觉得需要的时候定
(function arguments)
;式 3-1
四、 函数的定义 一般说来,函数通过 defun 系列函数来定义,语法如下:
(defun sym argument-list expr ...) ;式 4-1
① defun 函数定义一个名为sym的函数(defun是系统定义的一个函 数,其功能就是用来用户定义自己的函数)。
这里所说的"符号"包括函数名、参数名和变量名。 需要说明的是,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 中) 命令名是有独 立空间的。即便如此,我们也不建议这样用,一般说来,函数、变量, 尤其是命令的命名应 该能表达某种意义,向使用者和阅读者传递某种相 关的信息,而过度简单且不能引起联想的纯数字是无法承载这个任务 的。 从这个意义来说,函数名(或变量名)的命名每一个字节都应该仔
2. 匿名函数 在一下表达循环的函数(比如 mapcar、forearch 等)的使用中,我 们往往见到使用 lambda 定义的匿名函数,这些函数内容我们也可以作 为一个"模块"定义为一个有名函数,然后对函数名进行引用,不过那样 会占用更多的资源,相对而言也就不够效率了。
七、 函数的作用机理
函数在不同情况下使用,其作用机理也是不同的。根据其和"外界"的 交互作用的不同,笔者将函数的作用机理归结为"返回值作用"和"过程作 用" 。
函数同为数据对象,就意味着我们可以像对待其他对象那样把它传递 给其他函数。这种性质对于 AutoLISP 这种自底向上程序设计至关重要。 AutoLISP 函数语法的描述遵循如下
这是 AutoLISP 中系统自带函数的一般性语法,也包括用其它语言定 义的函数。
所有 AutoLISP 表达式的格式都如下所示:
可以经常调用:这是"函数"定义的一个基本要求,不能经常调用 就不必封装成函数了。
三、 AutoLISP 函数表达式 与很多的高级语言不同,函数也是AutoLISP(注:所有种类的Lisp
都是这样)的一种数据类型,这意味着在 AutoLISP 里我们可以像对待 其他熟悉的数据类型那样来对待函数,就像整数那样,在运行期创建一 个新函数,把函数保存在变量和结构体里面,把它作为参数传给其他函 数,还有把它作为函数的返回值。
义。
⑥ 除此之外,给符号名赋值的 set 族函数也可以用来定义函数,这
个问题将在后面会阐述。
⑦ 只有函数接受参数输入,如(setq tt '(* x 3)),tt是一个表,不是函
数,故(tt 3)是错误的。(setq tt +)从此以后tt代表函数+,故(tt 1
2 3)是正确的。
⑧ 表可以作为参数传递给其它函数,换言之,函数每个参数都可以
从以上意义上,笔者建议使用"程序名(或部分字母)+特殊字符+ 函数名"这种命名方式,如 ca_main、tr:trans等。
虽然没有明确限制, 但一些前缀在 AutoCAD 中有自己的意义。比 如"ai_"AutoCAD(随系统附带的扩展函数),"acet_" AutoCAD ExpressTools 附带的扩展函数)等,另外一些著名的第三方 Lisp 扩展 库,都会有自己的"专属"前缀,使用时也都应该注意避免。
是表。每个表都返回一个可由外层表达式使用的值。如果没有外
层表达式,AutoLISP 将则该值返回给AutoCAD 命令行。例如,
如下代码调用了三个函数:
(fun1 (fun2 arguments)(fun3 arguments))
;式 1-1
第一个函数 fun1 有两个参数,另两个函数 fun2 和 fun3 分别作为
((= se1 "T") (setq b_ 1) (ca5_tot)) ;转求和分支函 数 ca5_tot (t 在程序中,为了代码的可读性,有时把因多层嵌套而显得"臃肿"的大 段代码也定义成函数,从某种意义来说,这也应该归类于"模块"类,虽 然这是最不像函数的函数。 作为"模块"使用的函数,虽然多数只使用一次,并不"经济" ,但因 为这种方法的使用,使得原本凌乱的程序结构变得清晰,所以也还是值 得的。
(action_tile "ttj" "(tt_tk_ttj)") ;为对话框控件 ttj 指定动作函数 tt_tk_ttj 另一类是作为"模块"使用的典型函数是对命令选项定义的程序分支函 数。如:
例 6-2
(setq a_ 2 se1 (ca5 -ssget "\n 拾取钢筋直径或 [求和(T)]: " "T" '((0 . "TEXT")))) (cond
1. 返回值作用 按函数的一般概念,每个函数对应输入值都有一个最终计算结果, 这个最终结果也称返回值。 每个函数都有一个返回值,根据输入值(参数)的不同,返回值的结 果也不同,这个不同不仅是数值的不同,也可能包括数据类型等的不 同。 以返回值作为输出值的函数,在使用时一般需要对返回值进行"收集" 常见的 ,就是把返回值赋给一个变量,如: (setq y (mapcar )) ;式 7-1,把函数 mapcar 的返回值赋给变量
fun1 的一个参数,本身各有一个参数。函数 fun2 和 fun3 被函数 fun1 所包含,因此它们的返回值作为参数传递给 fun1。函数 fun1 由这两个参数计算函数值,并将该值返回给命令行。
五、 函数命名规则
AutoLISP 通过符号来引用数据。符号名不区分大小写,可以由字 母、数字和标注符号(除 ( ) . ' " ; 以外)的任何序列组成。符号名不能 仅由数字组成。
上式一对英文括号组成的表达式,称之为表,Lisp语言也称为表的语 言。表有两种形式,一种是“口袋式”表;一种是“函数式”表。前者 如’(0 0)表示一个2维点,前面加’表示此表不求值。如果不加‘,则通 常是认为是“函数式”表。“函数式”表如下:
① 结构特点:左括号(紧跟函数,函数所需要的参数,右括号)结束。 ② 函数是指:系统定义的函数、自定义的函数、匿名函数lambda。如
细斟酌。 对于我们这些少量编程的 AutoCAD 的用户而言,过度复杂的函数
名也会给使用带来不便,所以,只要有限的标识,起到便于识别的目的 即可。
除非声明成局部变量,否则函数一经定义并加载,函数会驻留在 AutoCAD 的内存中,不同程序的同名函数会相互干扰,造成程序不可 用或结果错误,因此,在函数的命名时,也应考虑不同程序间函数的相 互避让。
综合以上描述,我们可以看到"函数"的基本组成: 输入值:一般称为参数; 表达式:函数体,是代码的集合,共同组成上面所说的"法则";
输出值:表达式对应输入值的(唯一)结果,通常称为"返回值 ";
封装:不同的语言,封装的意义也不同,在开放式的 AutoLISP 语言中, "封 装"并不意味着编译或打包,只使之以完整的函数体 驻留在内存中也应可以称为"封装" ;
以上所说的是一般意义上的函数,也可以理解为有名函数,在 AutoLISP 中还有一种可以理解为"临时"定义的一次性的匿名函数 (lambda 表达式)即没有名字的函数,lambda 表达式基于数学上闭包 问题中的"演算"而得名,因为无名,所以无法重复调用,其它规则均不 违背。
六、 何时使用自定义函数
须在函数名后提供一对空括号或者 nil (注:isp 语言中 nil 和一 对空括号是等价的)。不管是否需要声明参数,但"参数列表"项 是必须的,这是函数区别于赋值的一个重要特点。 ③ expr 为函数sym执行时的AutoLISP 表达式,可以是数字(整数、 实数)、字符串、T、nil、表、函数。见如下例 4-2 ④ AutoLISP 定义的函数本身是无法包括"可选参数"的,函数定义 时,参数数量一经声明,使用时就无法增减,否则就会出现"参数 太多(少)"的错误。例如: 例 4-1 (defun tan(x) ;定义一个正切函数,函数名 tan,有一个参数 x (/ (sin x) (cos x)) ;函数体 ) 使用这个函数时,必须而且只能输入一个变量,否则出错。而系统 自己定义的函数,参数的数量可是变的,如+。
AutoLISP 基础——认识自定义函数
(本文由LL_J“认识自定义函数”和“Autolisp编程心得”两篇巨著合成, 并融入了其他人的一些经验,以快速打通你的任督二脉——自贡黄明儒注)
一、 初识Lisp 在AutoCad命令输入(+ 1 2 3),回车返回6,如下
命令: (+ 1 2 3) 6
恭喜你,你已经会写Lisp程序了。这里,我们用到了一个系统定义的 函数+,这个函数的作用就是对后面的数字求和。
二、 什么是函数 函数一词最早来源于数学,维基百科这样说:函数是将唯一的输出
值赋予每一输入的"法则"以及该输出值与对应输入值的集合。 在计算机领域,对函数并没有一个完整的定义,百度百科有这样一
句话:"许多程序设计语言中,可以将一段经常需要使用的代码封装起 来,在需要使用时可以直接调用,所以,函数也可以说是许多代码的集 合,这就是程序中的函数。
有重复使用代码的地方,定义函数是必要的,通常可以提高运行速 度。
1. 作为"模块"的自定义函数 程序应该有良好的结构,不仅仅是便于阅读,同时也对程序的运行 效率有一定影响。多数的高级语言通过模块和转移(goto)来完成程序 的结构化,和这些高级语言 不同的一点,AutoLISP 可以通过自定义函 数来完成这些要求,不需要编译就可以直接使用的代码,相比其他语言 来说,更具灵活性。作为"模块"使用的典型的自定义函数主要有两类, 一类是为对话框控件定义的动作函数,如: 例 6-1
((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两个参数。
例 4-2 (defun absv(x) ;定义一个绝对值函数
Fra Baidu bibliotek
(if (>= x 0)
x ;单一变量表达式
(- x)
)
)
⑤ 如果我们需要定义一个匿名函数,则可以使用 lambda 函数,除
函数名"缺失" 外,其它都和 defun 相同。匿名函数在使用时定
义,普通的函数则可以在使用前,或任何你觉得需要的时候定
(function arguments)
;式 3-1
四、 函数的定义 一般说来,函数通过 defun 系列函数来定义,语法如下:
(defun sym argument-list expr ...) ;式 4-1
① defun 函数定义一个名为sym的函数(defun是系统定义的一个函 数,其功能就是用来用户定义自己的函数)。
这里所说的"符号"包括函数名、参数名和变量名。 需要说明的是,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 中) 命令名是有独 立空间的。即便如此,我们也不建议这样用,一般说来,函数、变量, 尤其是命令的命名应 该能表达某种意义,向使用者和阅读者传递某种相 关的信息,而过度简单且不能引起联想的纯数字是无法承载这个任务 的。 从这个意义来说,函数名(或变量名)的命名每一个字节都应该仔
2. 匿名函数 在一下表达循环的函数(比如 mapcar、forearch 等)的使用中,我 们往往见到使用 lambda 定义的匿名函数,这些函数内容我们也可以作 为一个"模块"定义为一个有名函数,然后对函数名进行引用,不过那样 会占用更多的资源,相对而言也就不够效率了。
七、 函数的作用机理
函数在不同情况下使用,其作用机理也是不同的。根据其和"外界"的 交互作用的不同,笔者将函数的作用机理归结为"返回值作用"和"过程作 用" 。
函数同为数据对象,就意味着我们可以像对待其他对象那样把它传递 给其他函数。这种性质对于 AutoLISP 这种自底向上程序设计至关重要。 AutoLISP 函数语法的描述遵循如下
这是 AutoLISP 中系统自带函数的一般性语法,也包括用其它语言定 义的函数。
所有 AutoLISP 表达式的格式都如下所示:
可以经常调用:这是"函数"定义的一个基本要求,不能经常调用 就不必封装成函数了。
三、 AutoLISP 函数表达式 与很多的高级语言不同,函数也是AutoLISP(注:所有种类的Lisp
都是这样)的一种数据类型,这意味着在 AutoLISP 里我们可以像对待 其他熟悉的数据类型那样来对待函数,就像整数那样,在运行期创建一 个新函数,把函数保存在变量和结构体里面,把它作为参数传给其他函 数,还有把它作为函数的返回值。
义。
⑥ 除此之外,给符号名赋值的 set 族函数也可以用来定义函数,这
个问题将在后面会阐述。
⑦ 只有函数接受参数输入,如(setq tt '(* x 3)),tt是一个表,不是函
数,故(tt 3)是错误的。(setq tt +)从此以后tt代表函数+,故(tt 1
2 3)是正确的。
⑧ 表可以作为参数传递给其它函数,换言之,函数每个参数都可以
从以上意义上,笔者建议使用"程序名(或部分字母)+特殊字符+ 函数名"这种命名方式,如 ca_main、tr:trans等。
虽然没有明确限制, 但一些前缀在 AutoCAD 中有自己的意义。比 如"ai_"AutoCAD(随系统附带的扩展函数),"acet_" AutoCAD ExpressTools 附带的扩展函数)等,另外一些著名的第三方 Lisp 扩展 库,都会有自己的"专属"前缀,使用时也都应该注意避免。
是表。每个表都返回一个可由外层表达式使用的值。如果没有外
层表达式,AutoLISP 将则该值返回给AutoCAD 命令行。例如,
如下代码调用了三个函数:
(fun1 (fun2 arguments)(fun3 arguments))
;式 1-1
第一个函数 fun1 有两个参数,另两个函数 fun2 和 fun3 分别作为
((= se1 "T") (setq b_ 1) (ca5_tot)) ;转求和分支函 数 ca5_tot (t 在程序中,为了代码的可读性,有时把因多层嵌套而显得"臃肿"的大 段代码也定义成函数,从某种意义来说,这也应该归类于"模块"类,虽 然这是最不像函数的函数。 作为"模块"使用的函数,虽然多数只使用一次,并不"经济" ,但因 为这种方法的使用,使得原本凌乱的程序结构变得清晰,所以也还是值 得的。
(action_tile "ttj" "(tt_tk_ttj)") ;为对话框控件 ttj 指定动作函数 tt_tk_ttj 另一类是作为"模块"使用的典型函数是对命令选项定义的程序分支函 数。如:
例 6-2
(setq a_ 2 se1 (ca5 -ssget "\n 拾取钢筋直径或 [求和(T)]: " "T" '((0 . "TEXT")))) (cond
1. 返回值作用 按函数的一般概念,每个函数对应输入值都有一个最终计算结果, 这个最终结果也称返回值。 每个函数都有一个返回值,根据输入值(参数)的不同,返回值的结 果也不同,这个不同不仅是数值的不同,也可能包括数据类型等的不 同。 以返回值作为输出值的函数,在使用时一般需要对返回值进行"收集" 常见的 ,就是把返回值赋给一个变量,如: (setq y (mapcar )) ;式 7-1,把函数 mapcar 的返回值赋给变量
fun1 的一个参数,本身各有一个参数。函数 fun2 和 fun3 被函数 fun1 所包含,因此它们的返回值作为参数传递给 fun1。函数 fun1 由这两个参数计算函数值,并将该值返回给命令行。
五、 函数命名规则
AutoLISP 通过符号来引用数据。符号名不区分大小写,可以由字 母、数字和标注符号(除 ( ) . ' " ; 以外)的任何序列组成。符号名不能 仅由数字组成。
上式一对英文括号组成的表达式,称之为表,Lisp语言也称为表的语 言。表有两种形式,一种是“口袋式”表;一种是“函数式”表。前者 如’(0 0)表示一个2维点,前面加’表示此表不求值。如果不加‘,则通 常是认为是“函数式”表。“函数式”表如下:
① 结构特点:左括号(紧跟函数,函数所需要的参数,右括号)结束。 ② 函数是指:系统定义的函数、自定义的函数、匿名函数lambda。如
细斟酌。 对于我们这些少量编程的 AutoCAD 的用户而言,过度复杂的函数
名也会给使用带来不便,所以,只要有限的标识,起到便于识别的目的 即可。
除非声明成局部变量,否则函数一经定义并加载,函数会驻留在 AutoCAD 的内存中,不同程序的同名函数会相互干扰,造成程序不可 用或结果错误,因此,在函数的命名时,也应考虑不同程序间函数的相 互避让。
综合以上描述,我们可以看到"函数"的基本组成: 输入值:一般称为参数; 表达式:函数体,是代码的集合,共同组成上面所说的"法则";
输出值:表达式对应输入值的(唯一)结果,通常称为"返回值 ";
封装:不同的语言,封装的意义也不同,在开放式的 AutoLISP 语言中, "封 装"并不意味着编译或打包,只使之以完整的函数体 驻留在内存中也应可以称为"封装" ;
以上所说的是一般意义上的函数,也可以理解为有名函数,在 AutoLISP 中还有一种可以理解为"临时"定义的一次性的匿名函数 (lambda 表达式)即没有名字的函数,lambda 表达式基于数学上闭包 问题中的"演算"而得名,因为无名,所以无法重复调用,其它规则均不 违背。
六、 何时使用自定义函数