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

Nie*_*lsK 12 clojure

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

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

 (-1 6 27)
Run Code Online (Sandbox Code Playgroud)

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

有没有,还是有其他惯用的方法来解决这样的情况?

编辑:

另一种形式可能是

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

 (-1 6 27)
Run Code Online (Sandbox Code Playgroud)

由于评估,这让我感到害怕.

Mic*_*ent 10

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

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

这告诉我们

(map trampoline [- + *] [1 2 3] [1 2 3] [1 2 3])
;=> (-1 6 27)
Run Code Online (Sandbox Code Playgroud)

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

[(trampoline - 1 1 1), (trampoline + 2 2 2), (trampoline * 3 3 3)]然后变成 [-1 6 27](精确地以lazyseq的形式).

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

  • 我投票赞成是一个非常有趣的答案,但我不建议以这种方式解决它! (2认同)

Mat*_*ick 7

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

(map #(apply %1 %&) [- + *] [1 2 3] [1 2 3] [1 2 3])
Run Code Online (Sandbox Code Playgroud)

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


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


mik*_*era 6

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

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

(map apply [- + *] [1 2 3] [1 2 3] [1 2 3] (repeat nil))
=> (-1 6 27)
Run Code Online (Sandbox Code Playgroud)

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

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

(map funcall [- + *] [1 2 3] [1 2 3] [1 2 3])
=> (-1 6 27)
Run Code Online (Sandbox Code Playgroud)