011 没人先生学习Lisp函数
没人先生
没人先生一直都在, 但是没人注意到他. 他从头到尾一直都在跟大家一起学习Lisp.
- 001 粗鲁先生Lisp再出发
- 002 懒惰先生的Lisp开发流程
- 003 颠倒先生的数学表达式
- 004 完美先生的完美Lisp
- 005 好奇先生用Lisp来探索Lisp
- 006 好奇先生在Lisp的花园里挖呀挖呀挖
- 007 挑剔先生给出终止迭代的条件
- 008 挠痒痒先生建网站记
- 009 小小先生学习Lisp表达式
- 010 聪明先生拒(ji)绝(xu)造轮子
他感觉他跟Lisp中的函数很像, 天天都在, 每次都要用, 却还是有好多东西是不知道的. 他决定要好好学习一下Lisp中的函数.
函数的返回值
函数式编程一个重要的内涵就是值和值之间的替换,这个在Lisp中体现的淋漓尽致.每个函数的调用,放到一个表达式中,都与直接把函数的返回值放在那里是等价的.
函数可以返回任何数量以及任何类型的Lisp对象, 包括函数对象. 当函数返回多个值时, 推荐使用values
来构造返回值, 而不是使用list
, 当返回值是values
,
可以直接使用第一个值, 也可以用multiple-value-bind
来绑定返回值.
绑定多个值的方式如下:
1(multiple-value-bind (a b c) (values 1 2 3)
2 (list a b c))
记得我们可以把一个值替换为另外一个值
1(defun values-func ()
2 (values 1 2 3))
3
4(multiple-value-bind (a b c) (values-func)
5 (format t "a: ~a, b: ~a, c: ~a~%" a b c))
6;; a: 1, b: 2, c: 3
7;; NIL
8
9(multiple-value-list (values-func))
10;; (1 2 3)
当我们直接使用上面的values-func
时, 只会返回第一个值, 也就是1
.
1(values-func)
2;; 1
如果我们使用explore-lisp
来看Common-Lisp
中的函数定义, 哪些返回空值的函数, 会返回(values)
, 而返回多个值的函数, 会返回(values ...)
. 照着这个来抄总是没错的.
就比如, floor
函数的返回值是两个, 第一个是integer
, 第二个是real
, 这个是通过values
来返回的. 请仔细看Delcared type
和Derived type
两个字段的描述.
1(describe 'floor)
2COMMON-LISP:FLOOR
3 [symbol]
4FLOOR names a compiled function:
5 Lambda-list: (NUMBER &OPTIONAL (DIVISOR 1))
6 Declared type: (FUNCTION (REAL &OPTIONAL REAL)
7 (VALUES INTEGER REAL &OPTIONAL))
8 Derived type: (FUNCTION (T &OPTIONAL T)
9 (VALUES (OR NULL INTEGER) NUMBER &OPTIONAL))
10 Documentation:
11 Return the greatest integer not greater than number, or number/divisor.
12 The second returned value is (mod number divisor).
13 Known attributes: foldable, flushable, unsafely-flushable, movable
14 Source file: SYS:SRC;CODE;NUMBERS.LISP
15NIL
最后,我们还可以直接使用nth-value
来获取返回值的第n个值.
1(nth-value 1 (values 1 2 3))
2;; 2
这个函数就很好地配合函数式编程的思想,可以很容易进行值和表达式的替换,而不需要引入额外的变量.
函数的参数
函数的参数定义方式:
- 必须参数;
&optional
可选参数;&key
关键字参数;&rest
剩余参数;&allow-other-keys
允许其他关键字参数;
1(defun test-func (a &optional b c &key d e &allow-other-keys)
2 (list a b c d e))
这里的optional
和key
可以同时使用,SBCL可能会报警,但是不影响使用.
产生函数
根据CLHS的定义,函数是一种表示当提供恰当个数的参数即可以执行的代码的对象.函数有几种来源:
- 函数表达式:
function special form
; - 函数转换:
function coerce
- 函数编译:
function compile
函数special form
special form
是一种特殊的语法形式, 这玩意看起来像是函数, 但却是解释器特殊处理的语法结构. 例如if
和cond
就是special form
. 得益于聪明先生的聪明举动,可以在special operator中查看所有的special form
, 一共有25
个.
这里面的有一个function
,是一个special form
, 用来产生函数对象的.
1(function name)
或者使用#'
来简写:
1#'name
因为Lisp的函数和变量可以用一样的符号,所以这个#'
就是为了区分函数和变量的.
此外,function
还可以接受一个lambda
来产生一个函数对象.
1(setf func (function (lambda (x) (+ x 1))))
2(func 1)
3;; 2
总的来说,这个很平凡,我们记得在访问函数的时候明确使用#'
就可以了.
函数coerce
这个函数是强制类型转换的意思,可以将一个对象转换为另外一个对象. 当我们使用coerce
来转换一个函数对象时,会返回一个函数对象.
If the result-type is function, and object is any function name that is fbound but that is globally defined neither as a macro name nor as a special operator, then the result is the functional value of object. If the result-type is function, and object is a lambda expression, then the result is a closure of object in the null lexical environment.
1; 第一种情况,把一个函数名(不能是宏名或者特殊操作符)转换为函数对象,结果返回的是一个函数对象.
2(coerce 'car 'function)
3
4; 第二种情况,把一个lambda表达式转换为函数对象,结果返回的是一个**闭包**,这个闭包是在一个空的词法环境中的.
5(coerce #'(lambda (x) (+ x 1)) 'function)
函数compile
大部分函数都是表现为这一类,例如,文件被加载, 其中的函数就是这样,还可以直接调用compile
来编译一个函数.
1(defun test-func (x)
2 (+ x 1))
3
4(compile 'test-func)
5
6(compiled-function-p #'test-func)
7;; T
函数调用
调用函数的三种方式:
funcall
;apply
;multiple-value-call
.
funcall
funcall
调用一个函数对象时,第一个参数是函数对象,后面的参数是函数的参数.
1(funcall #'(lambda (x) (+ x 1)) 1)
2;; 2
apply
apply
调用一个函数对象时,函数对象之后的参数是一个列表,这个列表的元素是函数的参数.
1(apply #'(lambda (x y) (+ x y)) '(1 2))
2;; 3
multiple-value-call
multiple-value-call
调用一个函数对象时,把每一个values
都分别展开,然后作为参数传递给函数.
1(multiple-value-call #'(lambda (x y) (values (+ x y) (- x y))) 1 2)
2;; 3 -1
3
4(multiple-value-call #'+ (values 3 4) 2 1 (values 8 7 3))
5;; 28
总结
- 函数是一种对象,可以返回任何数量以及任何类型的Lisp对象, 包括函数对象;
- 函数的返回值推荐使用
values
来构造,此时直接调用返回第一个值,可使用multiple-value-bind
来绑定返回值,或者使用nth-value
来获取返回值的第n个值; - 函数的参数定义方式有必须参数,可选参数,关键字参数,剩余参数,允许其他关键字参数;
- 函数的产生方式有函数表达式,函数转换,函数编译;
- 函数的调用方式有
funcall
,apply
,multiple-value-call
; - 是谁学习了上面这些? 没有人.
文章标签
|-->lisp |-->function |-->values |-->apply |-->funcall |-->multiple-value-call |-->教程
- 本站总访问量:次
- 本站总访客数:人
- 可通过邮件联系作者:Email大福
- 也可以访问技术博客:大福是小强
- 也可以在知乎搞抽象:知乎-大福
- Comments, requests, and/or opinions go to: Github Repository