5

我对 CL 有点陌生,目前正试图围绕mapcan,#'funcall闭包。这是一个闭包,它将谓词应用于数字n,如果正确,则返回(list n),否则nil

(defun all-those (predicate)
  (lambda (n)
    (if (funcall predicate n) (list n))))

我知道我需要调用funcall才能将这个闭包变成一个函数。这工作正常:

>  (funcall (all-those #'evenp) 8)
(8)

现在我尝试将特此创建的函数作为参数传递给 mapcan:

>  (mapcan #'(funcall (all-those #'evenp)) '(1 2 3 4))

我得到一个编译时错误: (FUNCALL (ALL-THOSE #'EVENP)) is not a legal function name

但是,如果我省略#'以及funcall:

>  (mapcan (all-those #'evenp) '(1 2 3 4))
(2 4)

现在我很困惑。我的理解是,在使用符号的函数绑定 (*) 时需要对函数进行尖锐引用,并且在“关闭闭包”时mapcan需要调用。 是因为并且正在相互抵消,还是为什么我必须在上面的示例中省略它们?提前感谢您的任何回复。funcall

#'funcall


(*) 我知道在这个例子中我并没有真正可以遵循函数绑定的符号。但是如果我使用匿名函数,mapcan我仍然需要对它进行尖锐引用:(mapcan #'(lambda ...

4

2 回答 2

5

对于mapcarfuncall等,您需要传递函数对象或符号。如果您传递一个符号,则该符号的符号函数用作函数。如果你传递一个函数对象,那么它被用作函数。

您的所有这些函数都返回一个函数。这意味着(mapcan (all-those …) …)很好。

尖引号(#')只是函数形式的简写。也就是说,#'foo(function foo)相同:

function的值是 name 在当前词法环境中的函数值。

如果 name 是函数名,则该名称的函数定义是由最内层的词法封闭的 flet、标签或 macrolet 形式(如果有的话)建立的定义。否则返回函数名的全局函数定义。

如果 name 是一个 lambda 表达式,则返回一个词法闭包。在同一组绑定上的闭包可能不止一次产生的情况下,各种结果闭包可能是也可能不是等式。

因此,您只能使用#'或带有函数名称的函数这意味着要么是一个符号(例如,#'car),要么是一个 lambda 表达式(例如,#'(lambda (x) x))。这意味着以下内容不起作用(或者说真的没有意义,甚至):

#'(funcall (all-those #'evenp))

现在我很困惑。我的理解是,在使用 mapcan 来遵循符号的函数绑定 (*) 时,我需要对函数进行尖锐引用,并且在“关闭闭包”时需要调用 funcall。

mapcar的文档说它的第一个参数是:

function --- 一个函数的指示符,它必须接受与列表一样多的参数。

从词汇表:

功能代号n. 函数的代号;也就是说,一个表示函数的对象,它是以下之一:符号(表示在全局环境中由该符号命名的函数)或函数(表示自身)。如果将符号用作函数指示符,但它没有作为函数的全局定义,或者它具有作为宏或特殊形式的全局定义,则结果未定义。另请参见扩展功能指示符。

因此,您可以将函数直接传递给mapcarfuncall等,这正是您正在做的事情:

(mapcan (all-those …) …)

你也可以这样做:

(mapcan (lambda (x) ...) ...)
(mapcan #'(lambda (x) ...) ...)
(mapcan 'car ...)
(mapcan #'car ...)
(mapcan (symbol-function 'car) ...)
于 2016-11-10T21:35:46.660 回答
4

概括

(function foo)是一种特殊形式,返回一个函数对象。这里取回了名字foo。我们用它来获取一个函数对象。

(funcall foo)用于调用作为参数传递的函数 - 这里是 . 的变量值foo。funcall = FUNction CALL = 调用函数。我们用它来调用函数对象。

细节

这是一个闭包,它将谓词应用于数字 n,如果正确,则返回 (list n),否则返回 nil:

(defun all-those (predicate)
  (lambda (n)
    (if (funcall predicate n) (list n))))

不,这不是关闭。all-those返回一个闭包,但它本身不是一个。

? #'all-those
#<Compiled-function ALL-THOSE #x302000C9631F>

? (all-those #'evenp)
#<COMPILED-LEXICAL-CLOSURE (:INTERNAL ALL-THOSE) #x302000E5ECFF>

我知道我需要调用 funcall 将这个闭包变成一个函数。

闭包是一个函数对象。

? (functionp (all-those #'evenp))
T

注意:所有闭包也是函数对象。并非所有函数对象都是闭包。

闭包是一个函数和相关的变量绑定。它是一个函数对象。

请注意,匿名函数不一定是闭包。(function (lambda () ()))不返回闭包,因为没有变量绑定。你可以说它是一个带有空绑定的闭包,但在 CL 中它不被称为闭包。

请注意,在标准 Common Lisp 中,无法确定函数对象是否实际上是闭包,也无法通过变量名访问其绑定。

我知道我需要调用 funcall 将这个闭包变成一个函数。

funcall用于调用带有参数的函数对象(或将从符号的函数值中检索的函数对象)。

请记住,调用函数有多种方法:

  • 调用一个命名的全局函数:(foo bar baz)

  • 调用一个命名的词法函数: (foo bar bar)

  • 通过符号调用命名的全局函数:(funcall 'foo bar baz)

  • 从变量值调用函数对象:(funcall foo bar baz)

  • 从函数名(词法或全局)调用函数对象:(funcall #'foo bar baz)

  • 调用匿名函数对象:(funcall (function (lambda (foo) foo)) 'bar)(funcall #'(lambda (foo) foo) 'bar)(funcall (lambda (foo) foo) 'bar)

  • 调用匿名函数:((lambda (foo) foo) 'bar)

然后APPLY是类似于FUNCALL但从列表中获取参数。

(apply #'+ 1 2 3 (list 4 5 6))  is similar to  (funcall #'+ 1 2 3 4 5 6)

FUNCALL本身就是一个函数。将评估其所有参数形式。第一个参数需要计算为符号或函数对象。funcall将调用函数对象(或使用参数从符号函数值中检索的函数对象。

FUNCTION是一个特殊的运算符。它是语法。它本身不是一个函数。FUNCTION期望一个符号或一个 lambda 表达式作为它的子形式。表单返回一个FUNCTION函数对象,对应于符号(来自全局函数或词法函数)或lambda 表达式

? (defun foo (bar) (list bar 'baz))
FOO
? (function foo)        ; a function object from the global function
#<Compiled-function FOO #x302000CC0D1F>
? #'foo                 ; the same, differently written
#<Compiled-function FOO #x302000CC0D1F>
? (funcall #'foo 42)    ; calling the function object
(42 BAZ)
? (funcall 'foo 42)     ; calling the symbol-function of the symbol
(42 BAZ)
? (funcall (symbol-function 'foo) 42)    ; as above
(42 BAZ)
? (flet ((foo (arg) (list arg :foo)))   ; a local lexical function
    (list (foo 43)                      ; calling the local lexical function
          (funcall #'foo 42)            ; calling a function object,
                                        ; from the local lexical function
          (funcall 'foo 41)))           ; calling the symbol-function of the symbol
((43 :FOO) (42 :FOO) (41 BAZ))
于 2016-11-11T00:46:32.070 回答