Common Lisp函数的基础知识

正如上一篇文章所述..位置决定了对名字的解释..而在CL中..有很大一部分是函数的解释..


函数的名字一般只出现在三个地方:

1.函数调用..函数名字出现在列表的开头..后面跟的是其参数..(fun-name arguments*)..参数可以是任意的lisp表达式..这是函数出现最多的地方

2.函数定义..此处函数名字紧跟在defun之后..此时的作用是将函数名字与函数体绑定起来..此处是不对函数求值的..形式是(defun fun-name (argument*) body*)

3.操作函数..此处通过#'+函数名字获得与函数名字绑定的函数对象..此处也不求值..仅仅是获得对象来进行后续的操作..比如(manu-fun #'foo)..#'foo就得到了foo对应的函数对象..并作为参数传给了manu-fun来进行操作..其实#'是一种语法糖..是function的简略形式..#'foo等价于(function foo)..用以获得可以操作的foo函数体对象..如果manu-fun的第一个参数是fn..那么现在的fn就是foo函数..而不是对foo函数求值的结果..


其实可以把操作函数的操作想成C中的函数指针..只不过在CL中不需要单独定义这样的对象..而是可以直接拿来使用的..(因为CL是动态类型..强类型语言..)


函数定义:

(defun fun-name (argument*)

"fun-name documention"

 body*)

当调用时..首先对argument递归求值..然后实参转形参..再沿着body语句一句一句执行..最后返回最后一个语句的值作为函数的返回值..


其中..argument有四种匹配方式..大家应该都比较熟悉..

1.位置..就是先后顺序..

2.关键字..参数表中&key后的参数为关键字参数..在调用的时候..需要注明对应的关键字..比如参数列表:(..&key a b c)..调用的时候需要(:a a-value :b b-value :c c-value)..

3.可选参数..参数表中&optional后的参数为可选参数..也就是可以传进来也可以不传的参数..

4.可变参数..参数表中&rest后的参数..统一的打包为一个列表传入..一般&rest后只能跟一个参数..这个参数包含其后所有的参数..


很明显..现今的很多语言都有类似的参数机制..比如Python..Python其实更进一步了..不仅可以传列表(*)..同样可以传字典(**)..比CL还方便..


CL中还可以在函数定义时指定默认参数和是否赋值的标志..形式为(argument default-value assign-p)..default-value就是默认值..而assign-p是布尔值..如果被实参赋值了就为t..否则就为nil..默认参数可以利用前面的已经赋值过的其他参数..(这点也是很多语言类似)..


刚才说到..函数返回为最后一句..其实也有很多方法从中直接跳出..正式的方法是return-from操作符..形式为(return-from fun-name return-value)..我们可以使用其简化形式(return value)..但return在CL中的作用非常的小..



操作函数:

CL中的函数是作为一种特殊的对象存在的..他哦们通过前述的#'/function+fun-name来获取..(其实很想反射机制.通过字符串名称得到其实体)..获得的函数对象也可以做间接调用..这就需要两个特殊函数来帮忙了..

(funcall fun-object argument*) : funcall会把求值后的参数传递给fun-object..其实等价于(fun-name argument*)..

(apply fun-object argument argument-list) : apply其实类似..只不过它不拘泥于参数的表达..funcall中参数必须是一个一个的给出..但是apply提供了两种选择..一种是一个一个的给出..第二种则是包含到列表中去..它会把列表解开求值并传递给fun-object的..

注意..fun-object就是我们通过#'得到的对象..记住一点..这个对象和其他对象没有区别..同样可以赋值..传递等一系列操作的..


匿名函数:

就是lambda函数..其实我们很好理解这个..因为上面可以看到..函数名和函数对象之间是分割的绑定..我们可以单独使用函数名..也可以单独使用函数对象..而这个函数对象如果我们不去给名称..就成了lambda函数..

形式:(lambda (argument*) body*)..这其实和函数的定义是一致的..唯一的区别就是没有fun-name而已..

记住..lambda返回的实际就是函数对象本身了..所以我们是可以直接使用的..如((lambda (x) (print x)) 2)就直接打印2了..但是除了这个此种用途之外..lambda都是作为函数对象来使用的..比如(setf fun-name (lambda () ()))..此时fun-name就是函数对象了..而且此时(fun-name)就是错误的了..可以看到..作为函数对象..只有显式的lambda可以直接进行调用..其余都必须通过操作函数间接调用..

在传递函数对象的时候..我们同样可以直接传入lambda..比如上文中传入#'fun-name的地方.换成(lambda)同样没有问题..但是为了某些因素的考虑(个人觉得可能是可读性)..一般都要使用#'获取到lambda函数对象而后进行传递..赋值..调用的..这应该是个约定俗成的规矩..

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值