我为什么要在Clojure中使用'apply'?

时间:2009-08-10 20:28:33

标签: lisp clojure

这就是Rich Hickey在其中一篇博文中所说的,但我不明白使用apply的动机。请帮忙。

  

Clojure和CL之间的一个很大区别是Clojure是一个Lisp-1,因此不需要funcall,apply仅用于将函数应用于运行时定义的参数集合。所以,(apply f [i])可写(f i)。

另外,“Clojure是Lisp-1”是什么意思,不需要funcall?我从未在CL中编程。

由于

6 个答案:

答案 0 :(得分:52)

你会使用 apply ,如果传递给函数的参数数量未知在编译时(抱歉,不知道Clojure语法是什么好吧,诉诸计划):

(define (call-other-1 func arg) (func arg))
(define (call-other-2 func arg1 arg2) (func arg1 arg2))

只要在编译时知道参数的数量,就可以直接传递它们,如上例所示。但是如果在编译时不知道参数的数量,你就不能这样做(好吧,你可以尝试类似的东西):

(define (call-other-n func . args)
   (case (length args)
      ((0) (other))
      ((1) (other (car args)))
      ((2) (other (car args) (cadr args)))
      ...))

但这很快就变成了一场噩梦。这就是 apply 进入图片的地方:

(define (call-other-n func . args)
   (apply other args))

它接受作为最后一个参数给出的列表中包含的任意数量的参数,并将作为第一个参数传递的函数调用 apply 并使用这些值。

答案 1 :(得分:40)

术语Lisp-1和Lisp-2指的是函数是否与变量位于同一名称空间中。

在Lisp-2(即2个名称空间)中,表单中的第一个项目将被评估为函数名称 - 即使它实际上是具有函数值的变量的名称。因此,如果要调用变量函数,则必须将变量传递给另一个函数。

在Lisp-1中,与Scheme和Clojure一样,评估函数的变量可以进入初始位置,因此您不需要使用apply来将其作为函数进行评估。

答案 2 :(得分:31)

apply基本上打开一个序列并将该函数作为单独的参数应用于它们。

以下是一个例子:

(apply + [1 2 3 4 5])

返回15.它基本上扩展为(+ 1 2 3 4 5),而不是(+ [1 2 3 4 5])

答案 3 :(得分:6)

使用apply将适用于多个参数的函数转换为适用于单个参数序列的函数。您还可以在序列之前插入参数。例如,map可以处理多个序列。此示例(来自ClojureDocs)使用map来转置矩阵。

user=> (apply map vector [[:a :b] [:c :d]])
([:a :c] [:b :d])

这里插入的一个参数是vector。因此apply扩展为

user=> (map vector [:a :b] [:c :d])

可爱!

PS要返回向量的向量而不是向量序列,请将整个内容包装在vec中:

user=> (vec (apply map vector [[:a :b] [:c :d]]))

虽然我们在这里,vec可以定义为(partial apply vector),但事实并非如此。

关于Lisp-1和Lisp-2:1和2表示在给定的上下文中名称可以表示的事物的数量。在Lisp-2中,您可以拥有两个具有相同名称的不同内容(函数和变量)。因此,无论哪一个都有效,你需要用一些东西来装饰你的程序,以表明你的意思。值得庆幸的是,Clojure(或Scheme ...)允许名称仅表示一件事,因此不需要这样的装饰。

答案 4 :(得分:2)

应用类型操作的通常模式是将运行时提供的函数与一组参数组合,同上。

我对clojure做得不够,能够对该特定语言的细微之处充满信心,以确定在这种情况下使用apply是否是必要的。

答案 5 :(得分:0)

Apply对协议很有用,特别是与线程宏结合使用。我刚发现了这个。由于您can't use the & macro to expand interface arguments at compile time,您可以应用不可预测大小的矢量。

因此,我将此作为一个记录之间接口的一部分,该记录包含有关特定xml文件的一些元数据和文件本身。

(query-tree [this forms]
  (apply xml-> (text-id-to-tree this) forms)))

text-id-to-tree是此特定记录的另一种方法,它将文件解析为xml拉链。在另一个文件中,我使用实现query-tree的特定查询扩展协议,指定要通过xml->线程的命令链。宏:

(tags-with-attrs [this]
  (query-tree this [zf/descendants zip/node (fn [node] [(map #(% node) [:tag :attrs])])])

(注意:这个查询本身会为没有的标签返回很多“nil”结果 属性。筛选并缩小以获得唯一值的清晰列表。)

顺便提一句,zf指的是clojure.contrib.zip-filter,并指向clojure.zip。 xml-> macro来自clojure.contrib.zip-filter.xml库,我:use