Clojure:组合arity 2(或更高)的函数
根据参数,我使用可变数量的函数处理数据。每个处理函数都将从其前一个函数接收数据,对其进行处理并将其传递给下一个函数Clojure:组合arity 2(或更高)的函数,clojure,functional-programming,function-composition,arity,Clojure,Functional Programming,Function Composition,Arity,根据参数,我使用可变数量的函数处理数据。每个处理函数都将从其前一个函数接收数据,对其进行处理并将其传递给下一个函数 (defn example [data] (do-things-to data)) 我的申请流程是 检查参数并将所需函数存储在向量中 创建一个包含所有必要步骤的函数 调用进行文件管理并最终应用该函数的包装器函数 模型: (let [my-big-fun (reduce comp (filter identity) vector-of-functions)] (wrappe
(defn example [data]
(do-things-to data))
我的申请流程是
(let [my-big-fun (reduce comp (filter identity) vector-of-functions)]
(wrapper lots-a-arguments big-fun)
现在我发现我不仅需要将数据传递给函数,还需要另一个数据集
(defn new-fun-example [root data]
(do-things-to-both root data))
有没有类似于我对arity-1函数所做的简化的方法?简单的juxt
不行,因为每个函数都会更改下一个函数所需的数据。返回'(根数据)
序列或类似序列需要在许多函数中进行大量重写
有什么想法吗?我猜答案是“宏”,但我从来没有摆弄过这些
编辑1:
第二个参数是对不断增长的图形数据结构的引用,因此它不需要由函数处理,只需以某种方式传递。
但是这些函数可能来自不同的名称空间,所以我不能简单地将根放在更高的范围内来访问它。一个全局的def
是可能的,但是非常难看
在写这篇文章时,我只是想在编译函数之前,以某种方式将函数映射到partial
编辑2:
过滤器标识
造成了很多混乱,这不是我的问题。我本来不应该把它包括在我的样品里。我按照敏捷的棕色狐狸的建议解决了任务,并为自己有时的默默无闻而道歉。最小解决方案,如示例:
(defn example [root data]
(swap! root + data))
(defn fn-chainer [vector-of-functions]
(let [the-root (atom 0)
; this filter step is just required to remove erroneously apperaring nils
; from the vector of functions - not part of the question
vector-of-functions (filter identity vector-of-functions)
; bake the atom to the functions
vector-of-functions (mapv #(partial % the-root) vector-of-functions)
; now chain each funcion's result as argument to the next one
my-big-fun (reduce comp vector-of-functions)]
; let the function chain process some dataset
(my-big-fun 5))
; test some function vectors
(fn-chainer [example])
=> 5 ; = 0 + 5
(fn-chainer [example example])
=> 10 ; = 0 + 5 +5
(fn-chainer [example nil example example nil nil])10
=> 20 ; = 0 + 5 + 5 + 5 + 5, nils removed
首先,我觉得这里发生了很多事情:
(过滤器标识)
是一个传感器,但没有说明其他FN返回传感器,或者如果wrapper需要一个传感器,并且鉴于其中一些传感器将接收两个参数,我们可以放心地说它们不是传感器。您可能想要(部分筛选标识)
或#(筛选标识%)
(减少函数的comp(过滤器标识)向量)
而不是(应用函数的comp(cons(部分过滤器标识)向量)
(let [root [1 2 3]
other-root [4 5 6]
vector-of-functions [(partial filter identity) example (partial my-fun-example root) (partial other-fun-example root other-root)]
my-big-fun (apply comp vector-of-functions)]
(wrapper lots-a-arguments big-fun))
编辑:对于上面的应用comp,我错误地使用了
reverse
,(reduce comp[fn1 fn2 fn3])
将返回与(apply comp[fn1 fn2 fn3])
相同的结果。当应用时,正如您在编辑中提到的,您确实可以将函数映射到具有根的新函数中:
(mapv #(partial % root) vector-of-functions)
我不知道如何用arity 2组合两个函数,因为每个函数只能产生一个输出。每个函数的输出是由两个项组成的序列吗?不,不是。另一方面,第二个输入(root
,在示例中)是常量,它是对数据结构根的引用。好问题。1.我只是过滤函数向量,这样我就不会在开发阶段2意外地将nil
s压缩。因为我是clojure的新手,从未完全掌握apply
:-)的概念,它会更适合吗?如果是,为什么?我只是在你输入答案时编辑了关于使用部分的想法,所以我很高兴你也这么建议。您的解决方案中唯一的“但是”是:向量是以编程方式填充的,因此我需要调用map
?要添加部分根
我不确定。Re“只需过滤函数向量”:那么函数向量中的元素可能为零,或者输入(或输出)数据序列中的元素可能在comp之后?如果是前者,我会在你到达这一点之前做过滤。函数的一个先决条件是函数的向量没有任何零。重新应用:请参阅。您可以使用(reduce comp…
,但在我看来,它不太惯用。但您会看到一些人的说法正好相反。YMMV。无论如何,我认为首先更清楚地了解(过滤器标识)
是很重要的。如果我们能把它从问题中去掉,那就更好了:)Re“向量是以编程方式填充的”:不管它是“以编程方式”(通过一个接一个地连接函数,通过concat较小的函数向量等)还是“手工”(通过向量文本)填充的,在这两种情况下,您都会在函数向量中引用向量
,因此除了(应用comp函数向量)
(或reduce
)之外,您似乎不需要地图或其他任何东西。啊,现在我了解您的担忧。无论如何,谢谢你的解释。