【问题标题】:Higher order programming with Lisp: Passing a function to mapcar?使用 Lisp 进行高阶编程:将函数传递给 mapcar?
【发布时间】:2012-01-08 03:39:23
【问题描述】:

我正在学习 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)))

【问题讨论】:

    标签: lisp common-lisp


    【解决方案1】:

    这绝对是可能的!您快到了。您刚刚遇到了 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 绑定,请使用 fletFlet 在我看来是愚蠢的,因为它不像 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。

    【讨论】:

      【解决方案2】:

      当然,你可以这样做:

      (defun foo (fn)
        (mapcar fn '(1 2 3)))
      

      例子:

      (foo #'(lambda (x) (* x 2)))
      (foo #'1+)
      (foo #'sqrt)
      (foo #'(lambda (x) (1+ (* x 3))))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-11
        • 1970-01-01
        • 2019-05-18
        相关资源
        最近更新 更多