Clojure:实现comp功能
4法律规定如下:Clojure:实现comp功能,clojure,Clojure,4法律规定如下: 编写一个函数,它允许您创建函数组合。参数列表应包含数量可变的函数,并创建一个函数,从右向左应用这些函数 (= [3 2 1] ((__ rest reverse) [1 2 3 4])) (= 5 ((__ (partial + 3) second) [1 2 3 4])) (= true ((__ zero? #(mod % 8) +) 3 5 7 9)) (= "HELLO" ((__ #(.toUpperCase %) #(apply str %) take) 5
编写一个函数,它允许您创建函数组合。参数列表应包含数量可变的函数,并创建一个函数,从右向左应用这些函数
(= [3 2 1] ((__ rest reverse) [1 2 3 4]))
(= 5 ((__ (partial + 3) second) [1 2 3 4]))
(= true ((__ zero? #(mod % 8) +) 3 5 7 9))
(= "HELLO" ((__ #(.toUpperCase %) #(apply str %) take) 5 "hello world"))
这里的\uuuu
应该由解决方案代替
在此问题中,不应使用函数comp
我找到的解决方案是:
(fn [& xs]
(fn [& ys]
(reduce #(%2 %1)
(apply (last xs) ys) (rest (reverse xs)))))
它起作用了。但是我真的不明白
reduce
在这里是如何工作的。它如何表示(应用f_1(应用f_2…(应用f_n-1(应用f_n参数))…)
?让我们试着分三个阶段修改该解决方案。每个阶段都停留一段时间,看看您是否得到了它。如果这样做,请停止,以免我更迷惑您
首先,让我们有更多的描述性名称
(defn my-comp [& fns]
(fn [& args]
(reduce (fn [result-so-far next-fn] (next-fn result-so-far))
(apply (last fns) args) (rest (reverse fns)))))
然后考虑一些因素
(defn my-comp [& fns]
(fn [& args]
(let [ordered-fns (reverse fns)
first-result (apply (first ordered-fns) args)
remaining-fns (rest ordered-fns)]
(reduce
(fn [result-so-far next-fn] (next-fn result-so-far))
first-result
remaining-fns))))
接下来将reduce替换为执行相同操作的循环
(defn my-comp [& fns]
(fn [& args]
(let [ordered-fns (reverse fns)
first-result (apply (first ordered-fns) args)]
(loop [result-so-far first-result, remaining-fns (rest ordered-fns)]
(if (empty? remaining-fns)
result-so-far
(let [next-fn (first remaining-fns)]
(recur (next-fn result-so-far), (rest remaining-fns))))))))
考虑这个例子:
(def c (comp f1 ... fn-1 fn))
(c p1 p2 ... pm)
调用c
时:
- 第一个
最右边的参数comp
应用于fn
参数p*
- 然后对上一步的结果应用
(……)fn-1
- 然后将
应用于上一步的结果,并返回其结果f1
- 首先,最右边的参数
应用于(最后一个xs)
参数:ys
(apply (last xs) ys)
- 其余参数反向输入
:reduce
(rest (reverse xs))
获取提供的初始结果和函数列表,并将函数迭代应用于结果:reduce
(reduce #(%2 %1) ..init.. ..functions..)
- 以下是
comp
的一个简单(我认为)定义:
(defn comp [& fs]
(reduce (fn [result f]
(fn [& args]
(result (apply f args))))
identity
fs))
嵌套的匿名函数一开始可能会使其难以读取,因此让我们尝试通过将它们拉出并给它们命名来解决这个问题
(defn chain [f g]
(fn [& args]
(f (apply g args))))
此函数chain
与comp
类似,只是它只接受两个参数
((chain inc inc) 1) ;=> 3
((chain rest reverse) [1 2 3 4]) ;=> (3 2 1)
((chain inc inc inc) 1) ;=> ArityException
comp
topchain
的定义非常简单,有助于分离reduce
为展会带来的内容
(defn comp [& fs]
(reduce chain identity fs))
它将前两个函数链接在一起,其结果是一个函数,然后将该函数与下一个函数链接,依此类推
因此,使用上一个示例:
((comp #(.toUpperCase %) #(apply str %) take) 5 "hello world") ;=> "HELLO"
仅使用chain
(无reduce
)的等效值为:
从根本上讲,reduce
是关于迭代的
它只是捕获了一个序列的迭代模式并积累了一个结果。我认为reduce
有一种神秘感,这实际上会使它比需要的更难理解,但如果你只是分解它,你肯定会得到它(并且可能会惊讶于你发现它有多么有用).我的解决方案是:
(fn [& fs]
(reduce (fn [f g]
#(f (apply g %&))) fs))
让我们尝试一下:
((
(fn [& fs]
(reduce (fn [f g]
#(f (apply g %&))) fs))
#(.toUpperCase %)
#(apply str %)
take)
5 "hello world"))
fs是一个函数列表:
#(.toUpperCase %)
#(apply str %)
take
第一次通过reduce,我们设置了
f <--- #(.toUpperCase %)
g <--- #(apply str %)
f <--- uppercase the result of apply str
g <--- take
f以下是我的解决方案:
(defn my-comp
([] identity)
([f] f)
([f & r]
(fn [& args]
(f (apply (apply my-comp r) args)))))
我更喜欢A.Webb的解决方案,尽管它的行为与comp
不完全一样,因为它在调用时不返回标识
。只需添加一个零算术体就可以解决这个问题。我真的很喜欢将comp分解为链并减少-很容易摸索。
#(f (apply g %&)) <---- uppercase the result of apply str
f <--- uppercase the result of apply str
g <--- take
#(f (apply g %&)) <---- uppercase composed with apply str composed with take
(defn my-comp
([] identity)
([f] f)
([f & r]
(fn [& args]
(f (apply (apply my-comp r) args)))))