这绝对是可能的!您快到了。您刚刚遇到了 Common Lisp 的双重命名空间,这可能需要很多时间来适应。我希望我能说一两件事,让 Common Lisp 的两个命名空间不那么混乱。
您的代码几乎是正确的。你写道:
(defun foo (fn seq)
(mapcar #'fn seq))
但是,那是为了什么?嗯,#' 是简写。我给你展开。
(defun foo (fn seq)
(mapcar (function fn) seq))
所以,#'symbol 是 (函数符号) 的简写。在 Common Lisp 中——你似乎知道——符号可以绑定到函数和变量;这是 Lispers 经常谈论的两个命名空间:函数命名空间和变量命名空间。
现在,function 特殊形式所做的是将函数绑定到一个符号,或者,如果您愿意,可以获取该符号在函数命名空间中的值。
现在,在 REPL 上,你写的显然是你想要的。
(mapcar #'car sequence)
将 car 函数映射到一系列列表。而car符号没有变量绑定,只有函数绑定,这就是为什么你需要使用(function ...)(或者它的简写,# ') 以获取实际功能。
你的 foo 函数不起作用,因为你作为参数传递它的函数被绑定到一个符号作为变量。试试这个:
(let ((fn #'sqrt))
(mapcar #'fn '(4 9 16 25)))
您可能期望列出所有这些数字的平方根,但它没有用。那是因为您使用 let 将平方根函数绑定到 fn 作为变量。现在,试试这段代码:
(let ((fn #'sqrt))
(mapcar fn '(4 9 16 25)))
令人愉快!这会将平方根函数作为变量绑定到 fn 符号。
所以,让我们去修改你的 foo 函数:
(defun foo (fn seq)
(mapcar fn seq))
将工作,因为fn是一个变量。让我们测试一下,以确保:
;; This will not work
(foo sqrt '(4 9 16 25))
;; This will work
(foo #'sqrt '(4 9 16 25))
第一个不起作用,因为平方根函数绑定到函数命名空间中的sqrt。因此,在第二个中,我们从符号中获取函数,并将其传递给 foo,后者将其作为变量绑定到符号 fn。
好的,那么如果你想将函数绑定到函数命名空间中的符号怎么办?好吧,对于初学者来说,defun 是永久的。如果您希望它是临时的,例如 let 绑定,请使用 flet。 Flet 在我看来是愚蠢的,因为它不像 let 那样工作。但是,我会举一个例子,让你看看。
(flet ((fn (x) (sqrt x)))
(mapcar fn '(4 9 16 25)))
不会起作用,因为 flet 没有将函数绑定到变量命名空间中的符号,而是绑定到函数命名空间中。
(flet ((fn (x) (sqrt x)))
(mapcar #'fn '(4 9 16 25)))
这会如你所愿,因为 flet 将该函数绑定到函数命名空间中的符号 fn。而且,只是为了推动函数命名空间的想法:
(flet ((fn (x) (sqrt x)))
(fn 16))
将返回 4。