Lisp语言:函数的可选参数,剩余参数以及关键字参数

上一篇有关Lisp函数的文章简单介绍了函数的定义和调用,其中使用的参数是一般的参数。为了方便大家,将那篇文章的链接列出:

http://blog.csdn.net/keyboardota/article/details/7642767

如上文所述,如果定义函数时定义的是一般的参数,调用函数时传入参数的数量必须和函数定义的参数的数量相同,参数太多或者太少都会导致程序错误。

这样的函数调用起来真是有点不灵活,无法根据需要灵活传入不同数量的参数。在Lisp中解决这个问题的方法有几种,它们是可选参数剩余参数关键字参数,分别对应不同的使用场景。


可选参数

就如同它的名字所说明的一样,如果一个函数的一部分参数是选的,就适合使用可选参数。

比如现实使用中有一个记录人员信息的函数,要求必须提供“身份证号”和“姓名”两项内容,同时可以附加提供“身高”和体重两项,则可以把“身份证号”和“姓名”定义为必须参数,而“身高”和“体重”可以定义为可选参数。这样函数调用的时候就比较灵活,如果用户提供了“身高”和“体重”两项内容则将则两项传入函数中,如果没有提供这些信息就忽略这两个参数。

可选参数的定义使用&optional关键字,后面跟随的就是可选参数,下面是一个简单的样例:

(defun function21 (a b &optional c d)
        (format *query-io* "parameter a is ~a ~%" a)
        (format *query-io* "parameter b is ~a ~%" b)
        (format *query-io* "parameter c is ~a ~%" c)
        (format *query-io* "parameter d is ~a ~%" d))

以上样例中function21的参数有4个,参数a和b是必须参数,而c和d是可选参数。

调用时可以这样使用:

(function21 "string1" "string2")
这时参数a的值为"string1",而参数b的值为“string2”,因为参数c和参数d没有提供,则c和d的值都是NIL


function21也可以这样调用:

(function21 "string1" "string2" "string3" "string4")
则a b c d的值依次为"string1" "string2" "string3" "string4"


调用function21是参数必须大于等于2个,小于等于4个,下面两种调用方式都会报错:

(function21 "string1" ) ; 参数太少

(function21 "string1" "string2" "string3" "string4" "string5") ;参数太多


函数function21的测试结果如下:


在可选参数的使用过程中可以给参数设置缺省值,当函数调用过程中该参数没有提供时,这个参数的值会被赋予缺省值。

参数的缺省值通过一个列表的形式提供,如(c "default c" c-supplied-p),其中第一个元素c是参数名,第二个元素“default c”是缺省值,第三个元素用于判断参数是缺省值还是用户在调用时提供的。

对于第三个元素有些人会有些疑惑,为什么需要这个元素呢?不是可以通过比较判断来确定参数是否是缺省值吗?比如上面的定义就可以通过比较c和“default c”来确定参数c是否为缺省值。产生这个疑惑是因为忽略了一种情况,就是用户提供的参数恰好等于缺省值,如上面的情况中用户将c的值设置成“default c”,虽然c确实等于"default c",但是这个参数的值确实又是由调用用户提供的。这时就只能通过第三个元素来判断参数是否由调用用户提供,如果参数是有调用者提供的则第三个元素为真(T),如果调用用户没有提供这个参数则第三个元素为假(NIL)

下面是一个提供缺省值的可选参数的定义样例:

(defun function22 (a b &optional (c "default c" c-supplied-p) d)
        (format *query-io* "parameter a is ~a ~%" a)
        (format *query-io* "parameter b is ~a ~%" b)
        (format *query-io* "parameter c is ~a ~%" c)
        (format *query-io* "has parameter c: ~a ~%" c-supplied-p)
        (format *query-io* "parameter d is ~a ~%" d))

下面是该样例的测试结果,只要调用是没有提供参数c,则参数c的值为"default c",而c-supplied-p的值为假(NIL)




剩余参数

可选参数虽然比一般参数灵活,但是有些情况下还是不够应付实际情况,因为使用可选参数最终还是限制了参数的数量,函数调用者不能随意增加参数。

同样是录入人员信息的例子,如果需求有变化,需要函数还可以接受“肤色”和“职位”两项信息,用可选参数来实现的就需要修改。

如果有一种方法可以让函数接受无限多的参数,就可以让这个函数灵活面对不同的需求变化。这种方法就是剩余参数。在Lisp中可以通过&rest关键字为函数定义剩余参数,定义了剩余参数,函数调用过程中系统会将“剩余的参数”打包成一个列表传入函数中。所谓“剩余的参数”,就是去除必须参数后留下的其它参数。

下面是使用剩余参数的例子,函数function23中定义了a和b两个必须参数,同时定义了一个剩余参数other-parameters。调用function23过程中如果提供了超过两个的参数,第三个参数以后的所有参数会被打包成一个列表传给other-parameters.

(defun function23 (a b &rest other-parameters)
        (format *query-io* "parameter a is ~a ~%" a)
        (format *query-io* "parameter b is ~a ~%" b)
        (loop for parameter in other-parematers do
                (format *query-io* "    other parameter: ~a ~%" parameter)))

以上函数会打印参数a和参数b,然后遍历other-parameters列表,逐一打印后面的所以参数,不管后面有多少个参数,下面是function23的测试结果:



关键字参数

使用剩余参数虽然可以接受无限多个参数,不过在使用参数的过程中严重依赖于参数的顺序。

比如人员录入的函数中将第6个参数作为人员的“职位”信息,假如函数调用者只能提供“身份证号”,“姓名”和“职位”三项信息,在函数调用过程中也需要补足6项信息。调用过程可能是这样:(function-abc "4423xxx" "张三" "" "" "" "经理"),其中有三个参数纯粹是为了凑数。

这时候如果能直接指定参数的对应关系就会比较简单,比如(function-abc 身份证号:"4423xxx" 姓名:"张三" 职位:"经理") (只是样例而已,真正语法不是这样的)

在以上这种情况下使用关键字参数就能有效解决问题。

关键字参数通过&key来定义,&key后面跟参数名。定义了关键字参数后调用函数是可以使用 “:参数名 参数值”这种形式指定参数对应关系。

下面是关键字参数定义的样例,定义了a b c d这4个关键字参数。

(defun function24 (&key a b c d)
        (format *query-io* "parameter a is ~a ~%" a)
        (format *query-io* "parameter b is ~a ~%" b)
        (format *query-io* "parameter c is ~a ~%" c)

        (format *query-io* "parameter d is ~a ~%" d))

以上函数可以这样调用:

(function24  :a "string1"   :b "string2"  :c "string3"  :d "string4")

分别指定a b c d的值为"string1" "string2" "string3" "string4"


函数调用过程中某个参数是可以忽略的,如下面这样:

(function24  :a "string1"    :c "string3"  :d "string4")

其中参数b没有提供,程序不会报错,直接将b赋值为NIL


定义了关键字参数后,调用函数是参数的顺序是不重要的,可以像这样调用:

(function24  :a "string1"    :d "string4"  :b "string2")

其中参数b和参数d的位置反了,和定义时不同,不过程序可以照样正常执行。


当然也可以什么参数都不提供,直接通过(function24)来调用,其中a b c d四个参数的值都为NIL


函数调用过程中参数重复也是允许的,如下面的调用代码,参数d出现了两次也没有问题。在这种情况下参数的顺序又有点重要了,参数重复时,重复参数的第一个会起作用。就是说下面的调用参数d会等于"string2"

(function24  :a "string1"   :d "string2"  :c "string3"  :d "string4")

下面是函数function24测试的结果:



另外,关键字参数定义过程中也是可以指定缺省值的,指定方法和上面为可选参数定义缺省值的方法相同。具体就不再详述了,下面是样例和测试截图:


(defun function25 (&key a (b "default b" b-supplied-p) c d)
        (format *query-io* "parameter a is ~a ~%" a)
        (format *query-io* "parameter b is ~a ~%" b)
        (format *query-io* "has parameter b: ~a ~%" b-supplied-p)
        (format *query-io* "parameter c is ~a ~%" c)
        (format *query-io* "parameter d is ~a ~%" d))



通过以上几种特殊参数的使用,函数调用者在调用函数时可以传递不同数量的参数,甚至传递不同形式的参数。如果接触过面向对象编程,对这样的概念不会陌生,因为面向对象中经常会提到“重载”的概念,它的作用也是类似的。不过需要注意的是,这里不同参数的使用和“重载”是有区别的。Clisp并不支持同一函数名的多重定义,如果同一函数名在多个地方以不同形式定义,程序不会报错,不过只有后面定义的函数才会生效。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值