mapcan、尖锐的引用和闭包

mapcan, sharp quote and closures

我对 CL 比较陌生,目前正在努力研究 mapcan#'funcall 和闭包。 这是一个闭包,它将 predicate 应用于数字 n,如果正确,则 returns (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 跟随符号的函数绑定 (*) 时我需要对函数进行尖锐引用,并且在 "closing a closure".[=45 时我需要调用 funcall =]
是因为 #'funcall 互相抵消了还是为什么我必须在上面的例子中省略它们?预先感谢您的任何回复。


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

mapcarfuncall等,需要传递一个函数对象或者一个符号。如果你传递一个符号,那么符号的 symbol-function 被用作函数。如果你传递一个函数对象,那么它被用作函数。

你的所有那些函数returns一个函数。这意味着 (mapcan (all-those …) …) 没问题。

尖引号 (#') 只是 shorthand 用于 function 形式。也就是说,#'foo(function foo):

相同

The value of function is the functional value of name in the current lexical environment.

If name is a function name, the functional definition of that name is that established by the innermost lexically enclosing flet, labels, or macrolet form, if there is one. Otherwise the global functional definition of the function name is returned.

If name is a lambda expression, then a lexical closure is returned. In situations where a closure over the same set of bindings might be produced more than once, the various resulting closures might or might not be eq.

所以你只能使用 #'function 和函数名。这意味着一个符号(例如,#'car) or a lambda expression (e.g., #'(lambda (x) x))。这意味着以下内容不起作用(或者真的有意义,甚至):

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

Now I'm confused. It was my understanding that I need to sharp-quote a function when using mapcan to follow the symbol's function binding (*) and that I need to call funcall when "closing a closure".

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

function---a designator for a function that must take as many arguments as there are lists.

来自词汇表:

function designator n. a designator for a function; that is, an object that denotes a function and that is one of: a symbol (denoting the function named by that symbol in the global environment), or a function (denoting itself). The consequences are undefined if a symbol is used as a function designator but it does not have a global definition as a function, or it has a global definition as a macro or a special form. See also extended function designator.

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

(mapcan (all-those …) …)

您还可以这样做:

(mapcan (lambda (x) ...) ...)
(mapcan #'(lambda (x) ...) ...)
(mapcan 'car ...)
(mapcan #'car ...)
(mapcan (symbol-function 'car) ...)

总结

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

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

详情

Here is a closure, which applies a predicate to a number n and, if correct, returns (list n), else nil:

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

不,那不是闭包。 all-those return 是一个闭包,但它本身不是闭包。

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

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

I understand that I need to call funcall to turn this closure into a function.

闭包是一个函数对象。

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

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

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

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

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

I understand that I need to call funcall to turn this closure into a function.

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 形式 returns 函数对象,对应于符号(来自全局函数或词法函数)或 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))