我只是在学习ANSI Common Lisp(在Win32机器上使用clisp),我想知道mapcar是否可以使用传入的函数作为正式参数?请参阅以下内容:
(defun foo (fn seq)
(mapcar #'fn seq))
在我看来,这将提供比以下更大的灵活性:
(defun mult (i)
(* i 2))
(defun foo ()
(mapcar #'mult '(1 2 3)))
答案 0 :(得分:31)
绝对可能!你快到了。你刚刚碰到了Common Lisp的双重命名空间,这可能需要很多人习惯。我希望我可以说一两件事让Common Lisp的两个命名空间变得更加混乱。
您的代码几乎是正确的。你写道:
(defun foo (fn seq)
(mapcar #'fn seq))
但是,那是做什么的?好吧,#'是简写。我会为你扩展它。
(defun foo (fn seq)
(mapcar (function fn) seq))
因此,#'符号是(功能符号)的简写。在Common Lisp中 - 您似乎知道 - 符号可以绑定到函数和变量;这些是Lispers所说的两个名称空间:函数名称空间和变量名称空间。
现在,函数特殊形式的作用是将函数绑定到符号,或者,如果您愿意,还可以获取符号在函数名称空间中的值。
现在,在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,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 。而且,只是为了推动函数名称空间home的想法:
(flet ((fn (x) (sqrt x)))
(fn 16))
将返回4.
答案 1 :(得分:3)
当然,你可以这样做:
(defun foo (fn)
(mapcar fn '(1 2 3)))
示例:
(foo #'(lambda (x) (* x 2)))
(foo #'1+)
(foo #'sqrt)
(foo #'(lambda (x) (1+ (* x 3))))