定义一个新的函数
在Lisp中,通过宏defun以下面的结构来定义函数:
(defun name (parameter*)
"Optional documentation string."
body-form*)
这里值得注意的是函数的命名规则(lisp的函数名支持短横等字符):如果函数的目的是由a转为b,那么可以命名成:a->b这个形式,不过在lisp的世界中,a-b这种命名更符合lisp的美。作者这里比较了ab,a_b等形式(个人觉得,a_b形式还是靠谱的,尤其是学习多语言之后,可以有效的避免串了)
让我们从之前的hello world入手,解析下lisp中的函数定义方式:
(defun hello-world () (format t "hello, world"))
函数名为hello-world,参数列表为空,函数的主体是一个表达式:
(format t "hello, world")
再来一个复杂些的:
(defun verbose-sum (x y)
"Sum any two numbers after printing a message."
(format t "Summing ~d and ~d.~%" x y)
(+ x y))
函数名为verbose-sum,拥有x,y两个参数,有一段字符串说明,主体有两个表达式,并且第二个表达式的结果会作为函数的返回值。
参数列表
如同上面的verbose-sum函数,是最普通的参数定义,对函数调用方的参数值有着严格的一一对应关系,多了少了都会报错。下面逐个介绍其他类型的参数:
这类参数通常出现于存在不同角色调用同一个函数,而他们关注的点不相同的情况下。关键字: &optional,用法:
(defun foo (a b &optional c d) (list a b c d))
效果:
(foo 1 2) ==> (1 2 NIL NIL)
(foo 1 2 3) ==> (1 2 3 NIL)
(foo 1 2 3 4) ==> (1 2 3 4)
也支持特定默认值:
(defun foo (a &optional (b 10)) (list a b))
效果:
(foo 1 2) ==> (1 2)
(foo 1) ==> (1 10)
在某些应用中,或许也会存在选择参数的默认值与特定参数保持某种关系的需求:
(defun make-rectangle (width &optional (height width)) ...)
针对默认值这种情况,或许有需要知道是否是使用的默认值的需求,那么会有这样一个字段来甄别:-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)
偶尔,也会有这样的需求,允许传入n个参数,n为非负整数。例如神奇的加法运算:
(+)
(+ 1)
(+ 1 2)
(+ 1 2 3)
...
你可以试试,这里也有一个字段对应: &rest ,用法:
(defun format (stream string &rest values) ...)
(defun + (&rest numbers) ...)
这也是比较常用的参数形式(个人感觉极大的方便了代码的阅读),给参数一个更有意义的命名,由&key来实现:
(defun foo (&key a b c) (list a b c))
关键字参数同时也拥有可选参数的一些特性:
(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)
(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)
关键字参数还能绑定为其他关键字,例如:
(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)
这种方式用处独特,不过不常见。
正常来说,在一个函数中同时出现上面几种参数类型是不常见的,通常会导致一些奇怪的问题:
(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
忠告就是:如果有optional参数和key参数混用,最好全改成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)
so,混用参数需谨慎!
函数的返回值
就目前为止,俺们接触到的函数返回值都是由函数体的最后一个表达式自动返回的,这也是最常见的。不过通常也有函数体执行部分就直接返回的需求,这可以用特别字段RETURN-FROM来实现:
(defun foo (n)
(dotimes (i 10)
(dotimes (j 10)
(when (> (* i j) n)
(return-from foo (list i j))))))
以上代码即是获取符号目标的最小的i,j值。需要简单注意下RETURN-FROM字段的用法,它其后跟着的是函数名,然后是返回值。或许有些麻烦,这个在后面章节会有专门介绍。
高阶函数
这个名词意思与数学不太一样,主要是指"函数的参数也是函数"这么一种情况。
在lisp中,函数本身也是数据,是允许传递运行的。这里涉及到一个特殊字段:FUNCTION
例如:
(defun foo (x) (* 2 x))
则可以通过FUNCTION来获取函数:
(function foo)
#<Interpreted Function FOO>
FUNCTION还有一个缩写:#'。而在获取了函数后,有两个字段可以用于调用它:FUNCALL,APPLY,这二者之间仅在传递参数方式上有所区别。
funcall用于确定传递参数的个数及值的情况下:
(foo 1 2 3) === (funcall #'foo 1 2 3) //二者等价
在以下的应用中,funcall更有意义:
(defun plot (fn min max step)
(loop for i from min to max by step do
(loop repeat (funcall fn i) do (format t "*"))
(format t "~%")))
效果:
(plot #'exp 0 4 1/2)
*
*
**
****
*******
************
********************
*********************************
******************************************************
NIL
相对于funcall一次传一个参数,apply就简单多了,它允许传参数列表:
(apply #'plot #'exp plot-data)
这里最好写个实例来印证下。
匿名函数
某些情况下,我们需要用些功能,不过用defun来定义又有些"牛刀杀鸡"的感觉,于是lambda就出现了,定义一个匿名函数:
(lambda (parameters) body)
用法:
(funcall #'(lambda (x y) (+ x y)) 2 3) ==> 5
((lambda (x y) (+ x y)) 2 3) ==> 5
与普通函数的对比:
(defun double (x) (* 2 x))
(plot #'double 0 10 1)
-----
(plot #'(lambda (x) (* 2 x)) 0 10 1)
关于匿名函数的用法,在后面的章节还会陆续给出。
(未完待续)
分享到:
相关推荐
标题:“实用Common.Lisp编程.pdf” 描述:“实用Common.Lisp编程.pdf,2011.10出版” 从这些信息中,我们可以提炼出几个关键的知识点: ### Common Lisp语言简介 Common Lisp是一种高级的、通用的、多范式的编程...
common-lisp-the-language-second-edition.PDF
《Practical Common Lisp-1st-2005》是一本专注于Common Lisp编程语言的实用书籍,作者Peter Seibel通过这本书向读者展示了如何使用Common Lisp来解决真实世界中的问题,强调程序员作为工程师和艺术家的双重身份,而...
《Practical Common Lisp笔记》是一本深入探讨Common Lisp编程语言的实用教程。Common Lisp是一种功能强大的多范式编程语言,以其动态类型、宏系统和丰富的内置数据结构而闻名。这篇笔记详细记录了作者在学习过程中...
AUTOCAD二次开发lisp函数参考手册---由明经通道翻译
标题中的"emacs-lisp-intro-2.04.tar.gz"是一个典型的压缩文件名,它表明这个文件是一个关于Emacs Lisp的介绍性资料,并且版本号是2.04,格式为tar.gz。tar.gz是一种在Linux和Unix系统中常用的文件压缩格式,它先用...
《Common Lisp the Language, 2nd Edition》是Guy L. Steele所著的一本关于Common Lisp编程语言的权威指南,作为第二版,它不仅继承了前一版的经典,还对Lisp语言进行了深入的扩展和更新。这本书是Lisp爱好者的宝贵...
在压缩文件中,只有一个名为"practical_common_lisp.pdf"的文件,这是本书的PDF版本。由于原版PDF文件较大,经过7z压缩后,文件大小显著减小,便于下载和存储,但同时也能保持较好的图像和文字质量,满足读者的阅读...
Provides practical advice for the construction of Common Lisp programs. Shows examples of how Common Lisp is best used. Illustrates and compares features of the most popular Common Lisp systems on ...
这本《Practical Common Lisp》之所以号称Practical,正是因为这本书大量介绍Common Lisp在现实世界中的各种应用方式,算是第一本「入世传教」的Common Lisp著作。《Practical Common Lisp》是目前最畅销的Common ...
AutoLisp源文件--标注高程.LSP
ANSI Common Lisp 中文翻译版.pdf 此资源是 ANSI Common Lisp 的中文翻译版,涵盖了 Common Lisp 语言的基础知识和高级...7. ANSI Common Lisp 语言广泛应用于人工智能、机器学习、自然语言处理、计算机视觉等领域。
Common-Lisp.apk
计算多个数字之和、计算多条线段长度之和、插入墙高标注、查询多段线顶点坐标并绘制、自动生成页码、绘制示坡线、插入排水箭头 https://blog.csdn.net/qq_24141055/article/details/121446354
cad-lisp-3-表操作.LSP.lsp
标题中的“Lisp-music-player.rar”表明这是一个基于Lisp语言开发的音乐播放器软件,其源代码或可执行文件被压缩在RAR格式的文件中。RAR是一种流行的压缩格式,通常用于存储和分发多个文件,它允许用户将多个文件...
2. **函数式编程**:Common Lisp支持纯函数式编程风格,书中通过多个示例演示了如何利用这种风格编写简洁高效的代码。 3. **动态类型系统**:Common Lisp拥有灵活的类型系统,能够在运行时进行类型检查,这为开发者...
《AutoLISP函数参考手册-cad2008》是一部针对AutoLISP初学者和高级用户的权威指南,它涵盖了AutoLISP的主要函数、语法和编程技巧。 在AutoLISP中,函数是程序的核心部分,它们允许用户与AutoCAD的内核进行交互。...
在"AutoLISP函数参考【中文版】"中,包含了全面的AutoLISP函数说明,这对于进行CAD二次开发的工程师来说是不可或缺的资源。这个压缩包包含两个文件:`acad_alr.chm`和`_readme.txt`。 `acad_alr.chm`是一个帮助文件...