标准版本或惯用法(fn [f& args](apply f args))

时间:2011-11-18 13:12:50

标签: clojure

我经常发现自己想要在几个参数集合上应用函数集合。使用地图和非常简单的功能很容易。

(map
  (fn invoke [f & args] (apply f args))
  [- + *]
  [1 2 3]
  [1 2 3]
  [1 2 3])

 (-1 6 27)

搜索网络会出现很多定义类似功能的库,通常称为funcall或invoke。由于Clojure对可变函数的偏爱,我不禁想到应该已经有了这个函数的默认版本。

是否有或者是否有其他惯用的方法来解决这样的情况?

修改

另一种形式可能是

(map
  (comp eval list)
  [- + *]
  [1 2 3]
  [1 2 3]
  [1 2 3])

 (-1 6 27)

因为评估而让我害怕。

8 个答案:

答案 0 :(得分:10)

如果你真的没有关于功能名称的线索,但是你知道输入和输出必须是什么,你可以试试https://github.com/Raynes/findfn

(find-arg [-1 6 27] map '% [- + *] [1 2 3] [1 2 3] [1 2 3])
;=> (clojure.core/trampoline)

这告诉我们

(map trampoline [- + *] [1 2 3] [1 2 3] [1 2 3])
;=> (-1 6 27)

实际上,你可以在蹦床中滥用蹦床作为功能。但这不是惯用语,因为它是一个Lisp-1。上面的代码评估为:

[(trampoline - 1 1 1), (trampoline + 2 2 2), (trampoline * 3 3 3)]然后成为 [-1 6 27](确切地说是lazyseq的形式)。

正如Adrian Mouat在下面的评论中指出的,这可能不是解决它的首选方式。使用类似funcall的构造闻起来有点滑稽。必须有一个更清洁的解决方案。在你找到那个之前,findfn可以提供帮助;-)。

答案 1 :(得分:7)

编辑:这会做你想要的(正如@BrandonH所提到的):

(map #(apply %1 %&) [- + *] [1 2 3] [1 2 3] [1 2 3])

但这并不是对你的版本的改进 - 它只是使用匿名函数的简写。


我的理解是,在Common Lisp中FUNCALL是必需的,因为它是一个Lisp-2,而Clojure是一个Lisp-1。

答案 2 :(得分:6)

标准Clojure库中没有funcall或等效函数可以正常工作。 “apply”非常接近,但最后需要一组参数,而不是纯粹的变量。

考虑到这一点,你可以通过在末尾添加无限的nils列表(被认为是附加参数的空序列)来使用apply来“欺骗”以使其工作如下:

(map apply [- + *] [1 2 3] [1 2 3] [1 2 3] (repeat nil))
=> (-1 6 27)

总的来说,我觉得如果你真的想经常使用这个功能,明智的方法就是定义它:

(defn funcall [f & ps]
  (apply f ps))

(map funcall [- + *] [1 2 3] [1 2 3] [1 2 3])
=> (-1 6 27)

答案 3 :(得分:2)

(map #(%1 %2 %3 %4) [- + *][1 2 3][1 2 3][1 2 3])

(-1 6 27)

问题是,如果你想允许可变数量的参数,&语法将值放在向量中,需要使用apply。您的解决方案对我来说很好,但正如Brandon H指出的那样,您可以将其缩短为#(apply %1 %&)

正如其他回答者所说,它与funcall无关,我认为在其他Lisps中使用它来避免符号和函数之间的歧义(注意我在这里将函数称为(%1 ...) ,而不是(funcall %1 ...)

答案 4 :(得分:2)

我个人认为你的第一个版本非常清晰且惯用。

这是您可能会感兴趣的替代方案:

(map 
  apply 
  [- + *] 
  (map vector [1 2 3] [1 2 3] [1 2 3]))

=> (-1 6 27)

注意使用(map vector ....)将参数序列转换为([1 1 1] [2 2 2] [3 3 3])的技巧,以便它们可以在apply函数中使用

答案 5 :(得分:1)

另一种相当自我解释的方法:“对于每个第n个函数,将它应用于向量的所有第n个元素”:

(defn my-juxt [fun-vec & val-vecs]
  (for [n (range 0 (count fun-vec))]
    (apply (fun-vec n) (map #(nth % n) val-vecs))))

user> (my-juxt [- + *] [1 2 3] [1 2 3] [1 2 3])
(-1 6 27)

答案 6 :(得分:0)

我现在不能使用clojure.core函数,你可以插入地图并让它做你想做的事。所以,我会说,只使用你自己的版本。

Matt可能是正确的,因为没有funcall,是你几乎不需要它在Lisp-1中(意思是,函数和其他绑定在clojure中共享相同的名称空间)

答案 7 :(得分:0)

这个怎么样?它从juxt中选择相关的返回值。由于这都是懒惰的,它应该只计算所需的元素。

user> (defn my-juxt [fns & colls] 
        (map-indexed (fn [i e] (e i))
          (apply map (apply juxt fns) colls)))
#'user/my-juxt
user> (my-juxt [- + *] [1 2 3] [1 2 3] [1 2 3])
(-1 6 27)