第六章Common Lisp 指南-函数

 

第六章 Common Lisp 指南-函数

 

内容

l  返回函数的函数

l  科里化函数

 

返回函数的函数

“怎么写一个返回函数的函数呢?”是一个典型的问题,在学习Scheme语言的人中(这些人没有接初到LISP)普通存在。在SCHEME中,它们习惯这样做:

==> (define (adder n) (lambda (x) (+ x n)))

adder

 

==> ((adder 3) 5)

8

 

==> (define (doubler f) (lambda (x) (f x x)))

doubler

 

==> ((doubler +) 4)

8

在COMMON LISP中,这在很容易完成,但语法和语义不同。{dy}步,创建一个返回函数的函数,它看起来非常像,但除了一些很小的句法规则。但更深层的机理却不一样:

 

* (defun adder (n)

    (lambda (x) (+ x n)))

ADDER

 

上面的例子中,我们定义一个函数ADDER它返回一个函数的对象。为了创建这样一个类型,你不得不使用算符FUNCTION,并且将它应用到一个LAMBDA表达式。(FUNCTION form)也可以被略减为#’form.在上面的例子中,我们使用了一个简单的概念。如果不考虑这一点儿语法,我们可以将其这样来写:

* (defun adder (n)

    #'(lambda (x) (+ x n)))

ADDER

or

* (defun adder (n)

    (function (lambda (x) (+ x n))))

ADDER

 

不管我们如何写它,ADDER将返回一个函数,无论我们如何写它。但是我们不能像在SCHEME中一样使用它。

;;; continued from above

* (adder 3)

#<Interpreted Function "LAMBDA (N)" {485FFE81}>

 

* ((adder 3) 5)

In: (ADDER 3) 5

  ((ADDER 3) 5)

Error: Illegal function call.

 

这就是为什么:对于函数和变量,CL使用一个不同的名命空间。同样的符号,可代表不同的意义,这取决于它们被计算的窗体中的位置。

* (boundp 'foo)

NIL

* (fboundp 'foo)

NIL

* (defparameter foo 42)

FOO

* foo

42

* (boundp 'foo)

T

* (fboundp 'foo)

NIL

* (defun foo (x) (* x x))

FOO

* (fboundp 'foo)

T

* foo            ;;; ***

42

* (foo 3)        ;;; +++

9

* (foo foo)

1764

* (function foo)

#<Interpreted Function FOO {48523CC1}>

* #'foo

#<Interpreted Function FOO {48523CC1}>

* (let ((+ 3))

    (+ + +))

6

 

简单一点儿说,在COMMON LISP中,你可以将其视为,每个符号在CL中有两个存储位置。一个是单元:与值相关联,将某个值绑定到符号上,你可以使用函数BOUNDP来检测符号是否绑定到值。你能可以通过SYMBOL-VALUE来获得其值。

 

另一种单元,通常与函数相联系,代表着函数的定义。此种情形,符号被称作fbound,你可以通过FBOUNDP来测试一个符号是否是FBOUND的。你可以通过SYBMOL-FUNCTION来获得函数的存储单元。

 

好了,如果一个符号被评估了,它将被视为值单元返回的一变量,如上面被为***的部分。

如一个复合窗体被评估,并且它的car部分为一符号变量,那么函数单元将被使用,如上面被标为+++的部分。

在COMMON LISP中,不像SCHEME,被评估的复合窗体不是一个任意的窗体。如果它不是一个符号,那未它将是一个LAMBDA表达式,类似于

(lambda lambda-list form*)

 

这说明我们上面的错误,它即不是一个符号也不是一个lambda表达式。那,你也许会问,我们如何使用函数体(上面的ADDER返回的),答案是:使用FUNCALL或者是APPLY:

;;; continued from above

* (funcall (adder 3) 5)

8

* (apply (adder 3) '(5))

8

* (defparameter *my-fun* (adder 3))

*MY-FUN*

* *my-fun*

#<Interpreted Function "LAMBDA (N)" {486468C9}>

* (funcall *my-fun* 5)

8

* (*my-fun* 5)

Warning: This function is undefined:

  *MY-FUN*

 

注意了,上面返回的(ADDR 3)函数 体存储在*MY-FUN*值单元中。如果,我们想使用函数*MY-FUN*在复合窗体的CAR单元,我们不得不储存一些东西在它的函数单元里。

;;; continued from above

* (fboundp '*my-fun*)

NIL

* (setf (symbol-function '*my-fun*) (adder 3))

#<Interpreted Function "LAMBDA (N)" {4869FA19}>

* (fboundp '*my-fun*)

T

* (*my-fun* 5)

8

 

现在,我们准备定义DOUBLER:

* (defun doubler (f)

    (lambda (x) (funcall f x x)))

DOUBLER

* (doubler #'+)

#<Interpreted Function "LAMBDA (F)" {48675791}>

* (doubler '+)

#<Interpreted Function "LAMBDA (F)" {486761B1}>

* (funcall (doubler #'+) 4)

8

* (funcall (doubler '+) 4)

8

* (defparameter *my-plus* '+)

*MY-PLUS*

* (funcall (doubler *my-plus*) 4)

8

* (defparameter *my-fun* (doubler '+))

*MY-FUN*

* (funcall *my-fun* 4)

8

 

语句FUNCALL即不是函数本身,如#‘+,也不是存储单元里的函数符号,‘+。

上面的讨论都非常简单,我们甚至没有提及到宏,特殊窗体,符号宏,自评估体,以及词法环境。

 

科里化函数

一个相关的概念是currying,如果你以前有过函数式语言,你可能会熟悉它。在上面的概念之上,我们下面的内容将会非常容易:

* (declaim (ftype (function (function &rest t) function) curry)

           (inline curry))

NIL

* (defun curry (function &rest args)

    (lambda (&rest more-args)

      (apply function (append args more-args))))

CURRY

* (funcall (curry #'+ 3) 5)

8

* (funcall (curry #'+ 3) 6)

9

* (setf (symbol-function 'power-of-ten) (curry #'expt 10))

#

* (power-of-ten 3)

1000

 

DECLAIM语句只是对编译器的一个暗示,它能xxx的产生代码。

郑重声明:资讯 【第六章Common Lisp 指南-函数】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——