- 浏览: 12946 次
- 性别:
- 来自: 西安
最新评论
函数形参列表
关于函数名或文档字符串就没有更多可说的了,而本书其余部分将用很多篇幅来描述所有可在一个函数体里做的事情,因此就只需讨论形参列表了。
很明显,一个形参列表的基本用途是为了声明一些变量,用来接收传递给函数的实参。当形参列表是一个由变量名所组成的简单列表时,如同在verbose-sum里那样,这些形参被称为必要形参。当函数被调用时,必须为它的每一个必要形参都提供一个实参。每一个形参被绑定到对应的实参上。如果一个函数以过少或过多的实参来调用的话,Lisp就会报错。
但是,Common Lisp的形参列表也给了你更灵活的方式将函数调用实参映射到函数形参。除了必要形参以外,一个函数还可以有可选形参,或者也可以用单一形参绑定到含有任意多个额外参数的列表上。最后,参数还可以通过关键字而不是位置来映射到形参上。这样,Common Lisp的形参列表对于几种常见的编码问题提供了一种便利的解决方案。
5.3 可选形参
虽然许多像verbose-sum这样的函数只有必要形参,但并非所有函数都如此简单。有时一个函数将带有一个只有特定调用者才会关心的形参,这可能是因为它有一个合理的默认值。例如一个可以创建按需增长的数据结构的函数。由于数据结构可以增长,那么从正确性角度来说,它的初始尺寸就无关紧要了。那些清楚知道自己打算在数据结构中放置多少个元素的调用者们,可以通过设置特定的初始尺寸来改进其程序的性能,而多数调用者只需让实现数据结构的代码自行选择一个好的通用值就可以了。在Common Lisp中,你可以使用可选形参,从而使两类调用者都满意。不在意的调用者们将得到一个合理的默认值,而其他调用者们有机会提供一个指定的值。
为了定义一个带有可选形参的函数,在必要形参的名字之后放置符号&optional,后接可选形参的名字。下面就是一个简单的例子:
(defun foo (a b &optional c d) (list a b c d))
当该函数被调用时,实参被首先绑定到必要形参上。在所有必要形参都被赋值以后,如果还有任何实参剩余,它们的值将被赋给可选形参。如果实参在所有可选形参被赋值之前用完了,那么其余的可选形参将自动绑定到值NIL上。这样,前面定义的函数会给出下面的结果:
(foo 1 2) → (1 2 NIL NIL)
(foo 1 2 3) → (1 2 3 NIL)
(foo 1 2 3 4) → (1 2 3 4)
Lisp仍然可以确保适当数量的实参被传递给函数——在本例中是2到4个。而如果函数用太少或太多的参数来调用的话,将会报错。
当然,你会经常想要一个不同于NIL的默认值。这时可以通过将形参名替换成一个含有名字跟一个表达式的列表来指定该默认值。只有在调用者没有传递足够的实参来为可选形参提供值的时候,这个表达式才会被求值。通常情况只是简单地提供一个值作为表达式:
(defun foo (a &optional (b 10)) (list a b))
上述函数要求将一个实参绑定到形参a上。当存在第二个实参时,第二个形参b将使用其值,否则使用10。
(foo 1 2) → (1 2)
(foo 1) → (1 10)
不过有时可能需要更灵活地选择默认值。比如可能想要基于其他形参来计算默认值。默认值表达式可以引用早先出现在形参列表中的形参。如果要编写一个返回矩形的某种表示的函数,并且想要使它可以特别方便地产生正方形,那么可以使用一个像这样的形参列表:
(defun make-rectangle (width &optional (height width)) ...)
除非明确指定否则这将导致height形参带有和width形参相同的值。
有时,有必要去了解一个可选形参的值究竟是被调用者明确指定还是使用了默认值。除了通过代码来检查形参的值是否为默认值(假如调用者碰巧显式传递了默认值,那么这样做终归是无效的)以外,你还可以通过在形参标识符的默认值表达式之后添加另一个变量名来做到这点。该变量将在调用者实际为该形参提供了一个实参时被绑定到真值,否则为NIL。通常约定,这种变量的名字与对应的真实形参相同,但是带有一个-supplied-p后缀。例如:
(defun foo (a b &optional (c 3 c-supplied-p))
(list a b c c-supplied-p))
这将给出类似下面的结果:
(foo 1 2) → (1 2 3 NIL)
(foo 1 2 3) → (1 2 3 T)
(foo 1 2 4) → (1 2 4 T)
5.4 剩余形参
可选形参仅适用于一些较为分散并且不能确定调用者是否会提供值的形参。但某些函数需要接收可变数量的实参,比如说前文已然出现过的一些内置函数。FORMAT有两个必要实参,即流和控制串。但在这两个之后,它还需要一组可变数量的实参,这取决于控制串需要插入多少个值。+函数也接受可变数量的实参——没有特别的理由限制它只能在两个数之间相加,它可以对任意数量的值做加法运算(它甚至可以没有实参,此时返回0——加法的底数)。下面这些都是这两个函数的合法调用:
(format t "hello, world")
(format t "hello, ~a" name)
(format t "x: ~d y: ~d" x y)
(+)
(+ 1)
(+ 1 2)
(+ 1 2 3)
很明显,也可以通过简单地给它一些可选形参来写出接受可变数量实参的函数,但这样将会非常麻烦,光是写形参列表就已经足够麻烦了,何况还要在函数体中处理所有这些形参。为了做好这件事,还不得不使用一个合法函数调用所能够传递的那么多的可选形参。这一具体数量与具体实现相关,但可以保证至少有50个。在当前所有实现中,它的最大值范围从4096到536 870 911。 汗,这种绞尽脑汁的无聊事情绝对不是Lisp风格。
相反,Lisp允许在符号&rest之后包括一揽子形参。如果函数带有&rest形参,那么任何满足了必要和可选形参之后的其余所有实参就将被收集到一个列表里成为该&rest形参的值。这样,FORMAT和+的形参列表可能看起来会是这样:
(defun format (stream string &rest values) ...)
(defun + (&rest numbers) ...)
5.5 关键字形参
尽管可选形参和剩余形参带来了很大的灵活性,但两者都不能帮助应对下面的情形。假设有一个接受四个可选形参的函数,如果在多数的函数调用中,调用者只想为四个参数中的一个提供值,并且更进一步,不同的调用者甚至有可能将分别选择使用其中一个参数。
想为第一个形参提供值的调用者将会很方便——只需传递一个可选实参,然后忽略其他就好了。但是所有其他的调用者将不得不为所不关心的一到三个形参传递一些值。这不正是可选形参想来解决的问题吗?
当然是。问题在于可选形参仍然是位置相关的——如果调用者想要给第四个可选形参传递一个显式的值,就会导致前三个可选形参对于该调用者来说变成了必要形参。幸好我们有另一种形参类型,关键字形参,它可以允许调用者指定具体形参相应所使用的值。
为了使函数带有关键字形参,在任何必要的&optional和&rest形参之后,可以加上符号&key以及任意数量的关键字形参标识符,后者的格式类似于可选形参标识符。下面就是一个只有关键字形参的函数:
(defun foo (&key a b c) (list a b c))
当调用这个函数时,每一个关键字形参将被绑定到紧跟在同名键字后面的那个值上。如第4章所述,关键字是以冒号开始的名字,并且它们被自动定义为自求值常量。
如果一个给定的关键字没有出现在实参列表中,那么对应的形参将被赋予其默认值,如同可选形参那样。因为关键字实参带有标签,所以它们在必要实参之后可按任意顺序进行传递。例如foo可以用下列形式调用:
(foo) → (NIL NIL NIL)
(foo :a 1) → (1 NIL NIL)
(foo :b 1) → (NIL 1 NIL)
(foo :c 1) → (NIL NIL 1)
(foo :a 1 :c 3) → (1 NIL 3)
(foo :a 1 :b 2 :c 3) → (1 2 3)
(foo :a 1 :c 3 :b 2) → (1 2 3)
如同可选形参那样,关键字形参也可以提供一个默认值形式以及一个supplied-p变量名。在关键字形参和可选形参中,这个默认值形式都可以引用那些早先出现在形参列表中的形参。
(defun foo (&key (a 0) (b 0 b-supplied-p) (c (+ a b)))
(list a b c b-supplied-p))
(foo :a 1) → (1 0 1 NIL)
(foo :b 1) → (0 1 1 T)
(foo :b 1 :c 4) → (0 1 4 T)
(foo :a 2 :b 1 :c 4) → (2 1 4 T)
同样,如果出于某种原因想让调用者用来指定形参的关键字不同于实际形参名,那么可以将形参名替换成一个列表,令其含有调用函数时使用的关键字以及用作形参的名字。比如说下面这个foo的定义:
(defun foo (&key ((:apple a)) ((:box b) 0) ((:charlie c) 0 c-supplied-p))
(list a b c c-supplied-p))
可以让调用者这样调用它:
(foo :apple 10 :box 20 :charlie 30) → (10 20 30 T)
这种风格在想要完全将函数的公共API与其内部细节相隔离时特别有用,通常是因为想要在内部使用短变量名,而不是API中的描述性关键字。不过该特性不常被用到。
5.6 混合不同的形参类型
在单一函数里使用所有四种类型形参的情况虽然罕见,但也是可能的。无论何时,当用到多种类型的形参时,它们必须以这样的顺序声明:首先是必要形参,其次是可选形参,再次是剩余形参,最后才是关键字形参。但在使用多种类型形参的函数中,一般情况是将必要形参和另外一种类型的形参组合使用,或者可能是组合&optional形参和&rest形参。其他两种组合方式,无论是&optional形参还是&rest形参,当与&key形参组合使用时,都可能导致某种奇怪的行为。
将&optional形参和&key形参组合使用时将产生非常奇怪的结果,因此也许应该避免将它们一起使用。问题出在如果调用者没有为所有可选形参提供值时,那么没有得到值的可选形参将吃掉原本用于关键字形参的关键字和值。例如,下面这个函数很不明智地混合了&optional形参和&key形参:
(defun foo (x &optional y &key z) (list x y z))
如果像这样调用的话,就没问题:
(foo 1 2 :z 3) → (1 2 3)
这样也可以:
(foo 1) → (1 nil nil)
但是这样的话将报错:
(foo 1 :z 3) → ERROR
这是因为关键字:z被作为一个值填入到可选的y形参中了,只留下了参数3被处理。在这里,Lisp期待一个成对的关键字/值,或者什么也没有,否则就会报错。也许更坏的是,如果该函数带有两个&optional形参,上面最后一个调用将导致值:z和3分别被绑定到两个&optional形参上,而&key形参z将得到默认值NIL,而不声明缺失了东西。
一般而言,如果正在编写一个同时使用&optional形参和&key形参的函数,可能就应该将它变成全部使用&key形参的形式——它们更灵活,并且总会可以在不破坏该函数的已有调用的情况下添加新的关键字形参。也可以移除关键字形参,只要没人在使用它们。 一般而言,使用关键字形参将会使代码相对易于维护和拓展——如果需要为函数添加一些需要用到新参数的新行为,就可以直接添加关键字形参,而无需修改甚至重新编译任何调用该函数的已有代码。
虽然可以安全地组合使用&rest形参和&key形参,但其行为初看起来可能会有一点奇怪。正常地来讲,无论是&rest还是&key出现在形参列表中,都将导致所有出现在必要形参和&optional形参之后的那些值被特别处理——要么作为&rest形参被收集到一个形参列表中,要么基于关键字被分配到适当的&key形参中。如果&rest和&key同时出现在形参列表中,那么两件事都会发生——所有剩余的值,包括关键字本身,都将被收集到一个列表里,然后被绑定到&rest形参上;而适当的值,也会同时被绑定到&key形参上。因此,给定下列函数:
(defun foo (&rest rest &key a b c) (list rest a b c))
将得到如下结果:
(foo :a 1 :b 2 :c 3) → ((:A 1 :B 2 :C 3) 1 2 3)
发表评论
-
正交性orthogonality
2012-10-12 10:17 577正交一词在通信概念中广泛出现,在此重温这一数学概念: 什么是 ... -
词法作用域和动态作用域
2012-10-12 10:02 447词法作用域:会在函数的定义处检查变量的绑定值; 动态作用域: ... -
backquote
2012-08-18 11:32 1728反引用(backquote) 反引用 (backq ... -
lisp‘numeric
2012-08-17 14:32 557lisp numeric -
lisp iteration
2012-08-17 14:14 6141.Do (do ((temp-one 1 ( ... -
symbol结构
2012-08-16 10:26 606symbol结构 when two variabl ... -
use recursion
2012-08-13 14:32 631To solve a problem using recurs ... -
Dynamic Scope
2012-08-13 11:18 629lexical variables has lexical s ...
相关推荐
AutoLISP是一种基于LISP语言的编程环境,专为Autodesk的AutoCAD软件设计,用于扩展AutoCAD的功能和自定义工作流程。这个压缩包文件包含了关于AutoLISP编程的教程,对于想要掌握这一技能的用户来说是宝贵的资源。 在...
CAD Lisp是一种基于Lisp语言的编程工具,常用于AutoCAD软件中,用于扩展和自定义CAD的功能。在“CAD-lisp.rar”这个压缩包中,包含的是一系列CAD Lisp源代码,这些源代码主要用于实现CAD中的图层管理、对象修改以及...
AutoLISP是一种基于LISP语言的编程方言,专为Autodesk的AutoCAD软件设计,用于扩展和自动化CAD操作。此压缩包文件“AutoLISP函数参考(明经翻译版).rar”提供了一份全面的AutoLISP函数参考资料,由明经翻译,方便用户...
LISP(List Processing)是一种古老而强大的编程语言,它的设计思想独特,主要应用于人工智能、计算机科学、软件工程等领域。在给定的“lisp工具.rar”压缩包中,包含了一个LISP程序,用于辅助绘图工具,使得用户...
《autocad完全应用指南.autolisp+dcl+visual lisp程序设计篇》重点讲解autolisp、dcl、visual lisp三合一的autocad二次程序开发技术。《autocad完全应用指南.autolisp+dcl+visual lisp程序设计篇》分为4篇,共35章。...
AutoCAD 2010 AutoLISP参考手册是专为AutoCAD 2010设计者和开发者编写的,旨在帮助他们深入理解和利用AutoLISP语言进行程序开发。AutoLISP是一种基于LISP(列表处理)语言的编程环境,特别为AutoCAD定制,允许用户...
AutoLisp是一种基于Lisp语言的编程环境,专为Autodesk的AutoCAD软件设计,用于扩展和自动化CAD操作。这个入门教程将引导你逐步了解AutoLisp的基本概念、语法和功能,帮助你提升AutoCAD的使用效率。 首先,我们要...
ANSI Common Lisp 中文翻译版.pdf 此资源是 ANSI Common Lisp 的中文翻译版,涵盖了 Common Lisp 语言的基础知识和高级主题。该资源包含了 17 章节,从基础的列表、特殊数据结构、控制流程、函数、输入与输出、符号...
### .NET与LISP联合编程:实现现代与传统的融合 #### 概述 .NET作为一种主流且高效的开发平台,在工业设计、工程制图等领域被广泛采用。然而,在利用.NET平台进行CAD(Computer-Aided Design)软件的二次开发时,...
标题:“实用Common.Lisp编程.pdf” 描述:“实用Common.Lisp编程.pdf,2011.10出版” 从这些信息中,我们可以提炼出几个关键的知识点: ### Common Lisp语言简介 Common Lisp是一种高级的、通用的、多范式的编程...
标题中的“LISPforPLINE.rar_LISPforPLINE_cad_cad lisp_lisp_lisp 线”指的是一个与AutoCAD相关的LISP程序,主要用于处理“PLINE”,即多段线对象。这个程序是用LISP语言编写的,LISP是一种古老但功能强大的编程...
### Lisp Excel:斯坦福的研究者实现的 Lisp 功能与 Excel 的集成 #### 概述 在《Lisp and Symbolic Functionality in an Excel Spreadsheet: Development of an OLE Scientific Computing Environment》这篇论文...
在AutoCAD环境中,LISP(AutoLISP)是一种强大的编程语言,它允许用户自定义功能,提高工作效率。对话框在AutoLISP编程中扮演着重要角色,为用户提供友好的交互界面,使用户能够输入参数、选择选项等。本文将详细...
《autocad完全应用指南.autolisp+dcl+visuallisp程序设计篇》重点讲解autolisp、dcl、visuallisp三合一的autocad二次程序开发技术。《autocad完全应用指南.autolisp+dcl+visuallisp程序设计篇》分为4篇,共35章。第1...
《MIT的LISP程序员手册》是MIT计算中心与电子研究实验室共同出版的一份详尽的LISP编程系统指南。这份手册由John McCarthy等多位在LISP语言开发领域有着卓越贡献的专家共同编写,旨在为LISP程序员提供全面的指导和...
《autocad完全应用指南.autolisp+dcl+visuallisp程序设计篇》重点讲解autolisp、dcl、visuallisp三合一的autocad二次程序开发技术。《autocad完全应用指南.autolisp+dcl+visuallisp程序设计篇》分为4篇,共35章。第1...
LISP,全称为“List Processor”,是一种历史悠久的高级编程语言,以其独特的链表数据结构和符号处理能力闻名。这份名为“LISP源码800例”的资源集合,显然是为那些想要深入理解LISP编程的人准备的。它包含了800个...
Visual LISP是AutoLISP的一个扩展,提供了一个更友好的图形界面,用于编写、测试和调试LISP程序。 在AutoCAD中,AutoLISP语言被设计用来增强和扩展软件的功能。它允许用户创建新的命令,修改已有命令的行为,以及...
《Land of Lisp》和《Machine Learning in Action》是两本非常重要的IT图书,分别涵盖了Lisp编程语言和机器学习这两个核心领域。 首先,让我们深入探讨《Land of Lisp》。这本书由Conrad Barski撰写,旨在将读者...
CAD LISP24个源代码是一组用于CAD(计算机辅助设计)软件的二次开发资源,主要基于LISP语言。LISP是一种古老而强大的编程语言,因其在CAD领域中的灵活性和高效性,常被用于定制和扩展CAD软件的功能,如AutoCAD、CASS...