Clojure:实现comp函数

Pau*_*uli 5 clojure

4Clojure 问题58表述为:


编写一个允许您创建函数组合的函数.参数列表应该采用可变数量的函数,并创建一个从右到左应用它们的函数.

(= [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"))
Run Code Online (Sandbox Code Playgroud)

__应该由解决方案取代.

在这个问题中,comp不应该使用该功能.


我找到的解决方案是:

(fn [& xs]
  (fn [& ys]
    (reduce #(%2 %1)
            (apply (last xs) ys) (rest (reverse xs)))))
Run Code Online (Sandbox Code Playgroud)

有用.但我真的不明白reduce这里是如何运作的.它是如何代表的(apply f_1 (apply f_2 ...(apply f_n-1 (apply f_n args))...)

A. *_*ebb 9

让我们尝试分三个阶段修改该解决方案.保持每个人一段时间,看看你是否得到它.如果你做的话,请停下来,以免让你更加困惑.

首先,让我们有更多的描述性名称

(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)))))
Run Code Online (Sandbox Code Playgroud)

然后考虑一些

(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))))
Run Code Online (Sandbox Code Playgroud)

接下来用一个相同的循环替换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))))))))
Run Code Online (Sandbox Code Playgroud)


too*_*kit 8

我的解决方案是:

(fn [& fs]
  (reduce (fn [f g]
            #(f (apply g %&))) fs))
Run Code Online (Sandbox Code Playgroud)

让我们尝试:

((
  (fn [& fs]
    (reduce (fn [f g]
              #(f (apply g %&))) fs)) 

  #(.toUpperCase %) 
  #(apply str %) 
  take) 

  5 "hello world"))
Run Code Online (Sandbox Code Playgroud)

fs是功能列表:

#(.toUpperCase %) 
#(apply str %) 
take
Run Code Online (Sandbox Code Playgroud)

第一次通过减少,我们设置

f <--- #(.toUpperCase %)
g <--- #(apply str %)
Run Code Online (Sandbox Code Playgroud)

我们创建一个匿名函数,并将其分配给reduce函数的累加器.

#(f (apply g %&)) <---- uppercase the result of apply str
Run Code Online (Sandbox Code Playgroud)

下次通过reduce,我们设置

f <--- uppercase the result of apply str
g <--- take
Run Code Online (Sandbox Code Playgroud)

我们再次创建一个新的匿名函数,并将其分配给reduce函数的累加器.

#(f (apply g %&)) <---- uppercase composed with apply str composed with take
Run Code Online (Sandbox Code Playgroud)

fs现在为空,所以这个匿名函数从reduce返回.

这个功能通过5和"你好世界"

然后是匿名函数:

  • 取5个"你好世界"成为(\ h\e\l\l\o)
  • 是否适用于成为"你好"
  • 是否要成为"你好"


jbm*_*jbm 7

这是一个elegent(在我看来)定义comp:

(defn comp [& fs]
  (reduce (fn [result f]
            (fn [& args]
              (result (apply f args))))
          identity
          fs))
Run Code Online (Sandbox Code Playgroud)

嵌套的匿名函数可能会让它一开始难以阅读,所以让我们尝试通过拉出它们并给它们命名来解决这个问题.

(defn chain [f g]
  (fn [& args]
    (f (apply g args))))
Run Code Online (Sandbox Code Playgroud)

这个函数chain就像comp它只接受两个参数一样.

((chain inc inc) 1)              ;=> 3
((chain rest reverse) [1 2 3 4]) ;=> (3 2 1)
((chain inc inc inc) 1)          ;=> ArityException
Run Code Online (Sandbox Code Playgroud)

compatop 的定义chain非常简单,有助于隔离reduce为节目带来的东西.

(defn comp [& fs]
  (reduce chain identity fs))
Run Code Online (Sandbox Code Playgroud)

它将前两个函数链接在一起,其结果是一个函数.然后它将该功能链接到下一个,依此类推.

所以使用你的上一个例子:

((comp #(.toUpperCase %) #(apply str %) take) 5 "hello world") ;=> "HELLO"
Run Code Online (Sandbox Code Playgroud)

仅使用chain(否reduce)的等价物是:

((chain identity
        (chain (chain #(.toUpperCase %)
                      #(apply str %))
               take))
 5 "hello world")
;=> "HELLO"
Run Code Online (Sandbox Code Playgroud)

从根本上讲,reduce是关于迭代.这是命令式样式的实现可能是什么样的(忽略了多个arities的可能性,正如Clojure的版本支持):

def reduce(f, init, seq):
    result = init
    for item in seq:
        result = f(result, item)
    return result
Run Code Online (Sandbox Code Playgroud)

它只是捕获迭代序列并累积结果的模式.我认为reduce它有一种神秘感,它实际上可以让它比它需要的更难理解,但如果你只是分解它你肯定会得到它(并且可能会惊讶你经常发现它有用).