Lisp.定义函数

多数人通过defun来学习怎样定义函数。下面的表达式定义了一个叫做double的函数,这个函数返回它参数的两倍。

[1]> (defun double (x) (* x 2))
DOUBLE

将这些传递给Lisp之后,我们可以在其它函数中调用double,或者直接在最高层调用:

[2]> (double 1)
2

一个Lisp代码文件通常是由这样的defun来组成的,非常像C或者Pascal中的过程定义文件。但是一些非常不同的事情正在发生。那些defun不仅仅是过程定义,它们是Lisp调用。当我们探究defun内部发生的事情时,这个不同会变得明朗起来。


函数本身就是对象。defun真正做的就是将它存储在作为第一个参数传递过来的函数名下。所以我们既能够调用double,我们也可以获得实现它的函数。通常我们通过使用#'操作符来做这事。这个操作符可以被理解为将名字映射到真正的函数对象。通过将它放到double的前面

[2]> #'double 
#<FUNCTION DOUBLE (X) (DECLARE (SYSTEM::IN-DEFUN DOUBLE))
  (BLOCK DOUBLE (* X 2))>
[3]> 

我们获得了上面定义的真实的对象。这个的打印输出可能由于不同的实现而不同,一个common lisp函数是一级对象,它和熟悉的像是数字、字符串有相同的特征。所以我们可以将这个函数作为参数,返回它,将它存储在数据结构里等等。

[4]> (eq #'double (car (list #'double )))
T

我们甚至可以不用defun来定义函数。像是多数的Lisp对象,我们可以从字面上引用它们。当我们想要引用一个整形数,我就使用证书自身。要表示一个字符串,我们使用双引号包含着的一系列字符。要表示一个函数,我们使用叫做lambda表达式的东西。一个lambda表达式是个由三个部分组成的列表:符号lambda,一个参数列表,0个或者多个表达式。下面的这个lambda表达式和double是等价的:

[5]> (lambda (x) (* x 2))
#<FUNCTION :LAMBDA (X) (* X 2)>


它描述了一个接受参数x并返回2x的函数。


一个lambda表达式也可以被看作是函数的名字。通过将井号单引号防盗lambda表达式之前,我们得到对应的函数:

[6]> #'(lambda (x) (* x 2))
#<FUNCTION :LAMBDA (X) (* X 2)>

这个函数的行为和double是一样的,但是是两个不同的对象。


在一个函数调用中,函数的名称首先出现,然后是参数们:

[7]> (double 3)
6

既然lambda表达式也是函数的名字,那么它们也能够在函数调用中首先出现:

[8]> ((lambda (x) (* x 2)) 3)
6


在common lisp中,我们可以同时拥有函数double和变量double。

[9]> (setq double 2)
2
[10]> (double double)
4

当一个名字出现在函数调用第一个时,或者是前面有井号单引号时,它被看作是引用一个函数。否则的话,它就被当作变量名对待。


也就是说,common lisp中对于变量名和函数有不同的名字空间。我们可以有一个叫做foo的变量,和一个叫做foo的函数,不用标明它们。这种情况可能会引起混乱,并且导致大量丑陋的代码,但是这也是common lisp程序员必须适应的东西。


如果需要的话,common lisp提供了两个函数,将符号映射到它们所代表的值或者函数。函数symbol-value接受一个符号并返回对应变量的值:

[11]> (symbol-value 'double)
2

而symbol-function对于全局定义的函数做了同样工作:

[12]> (symbol-function 'double)
#<FUNCTION DOUBLE (X) (DECLARE (SYSTEM::IN-DEFUN DOUBLE))
  (BLOCK DOUBLE (* X 2))>


注意,既然函数是普通的数据对象,一个变量也可以用函数作为它的值:

[13]> (setq x #'append)
#<SYSTEM-FUNCTION APPEND>
[14]> (eq (symbol-value 'x) (symbol-function 'append))
T

表象之下,defun做的是将它第一个参数的symbol-function设置到剩余部分所组成的一个函数上。下面两个表达式做了同样的事情:

[15]> (defun double (x) (* x 2))
DOUBLE
[16]> (setf (symbol-function 'double) #'(lambda (x) (* x 2)))
#<FUNCTION :LAMBDA (X) (* X 2)>
[17]> 


所以defun和其它语言中的过程定义有一样的效果——将名字和一段代码关联起来。但是底层的机制是不同的。我们不需要defun来定义函数,函数也不一定要作为值存储在某些符号下面。defun的内部,类似于任何其它语言的过程定义,是一个更加泛化的机制:创建一个函数,将它与某个名字关联起来是两个独立的操作。当我们不需要Lisp的函数概念的完整的泛性时,defun使得函数定义像在其它更加严格的语言中一样简单。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
函数名称 描述 PL_3d? 如果多段线的检查是3D或不 PL_AddPoint 加入一个新的多段线点之前指定的顶点数量VxNum PL_Arced? 如果检查多段线有至少一个弧段 PL_ArcPl2LinearPl 重建更换指定的距离直线段只有所有弧段的多段线列表。 PL_BoundPoly 创建直线,多段线和/或圆周围的边界多边形 PL_BreakX 在这一点上打断多段线 PL_DelPoint 从多段线删除点 PL_DividedPoints 返回除以给定的多段线(无论是在“实体”的形式或列表的形式)所获得的点 PL_DrawLeaders 沿线各段多段线绘制的领袖,从而呈现出多段线的流动方向。 PL_DrawLeaders2 沿线各段多段线绘制两端箭头。 PL_facelist 返回从一个多面对象的面部定义列表 PL_Fitted? 检查是否有多段线在选集中 PL_FlipArcedPolyline 翻转(反转)的圆弧(2D)多段线使用特殊技巧 PL_Get2dPolyElev 找到一个二维多段线的标高 PL_GetBulgeLst 返回凸出圆弧多段线列表 PL_GetMiddlePoint 查找行的中间点,多段线,圆弧或样条线对象 PL_GetSlope @点 在某一时间点的斜率的曲线对象 PL_GetVxEntl 返回完整的实体,包括多段线顶点实体的实体列表的列表, PL_GetWidths 返回一个列表的开始和结束段多段线宽度 PL_MapMask 创建出多段线和/或圆形的不透明的面具,变成区实体。 PL_MeasuredPoints 返回通过测量给定的多段线(无论是在“实体”的形式或列表的形式)获得的分 PL_Mesh? 如果一个多段线对象的检查是一个三维网格对象或否 PL_mk_pl 给个列表,绘制多段线或LWPOLYLINE的entmake功能 PL_mk_pl_feed 给个列表,绘制多段线或LWPOLYLINE的,使用命令行坐标传输; PL_NewPoint 在指定的顶点更新多段线点 PL_Open? 判断多段线的检查是打开“或”不 PL_plist 返回一个LWPOLYLINE或多段线点表 PL_PlJoin 连接选择集中多段线或线段。 PL_plpick 多段线挑毛,挑个毛,看里面。 PL_PolyFace? 检查多段线对象是否是一个多面网格 PL_ReconstructPoly 重建炸开的多段线对象 PL_SeekMain 寻求从顶点名主要实体名称 PL_Spl2Pl_Int 转换到一个正常的多段线样条或ELLISPE对象的插值方法 PL_Splined? 检查是否拟合多段线 PL_SplitPoly 拟合多条多段线。 PL_SplPl2Pl 拟合多段线转换到正常的多段线 PL_UpdateLine 更新与两个新端点的线对象。保持所有行属性,包括句柄 PL_UpdatePoly 更新一个新的顶点列表的多段线实体。保留所有多段线特性:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值