我经常发现自己想要在几个参数集合上应用函数集合。使用地图和非常简单的功能很容易。
(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)
因为评估而让我害怕。
答案 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)