Clojure: comp function implementation

4Clojure Problem 58 is defined as:


Write a function that allows you to create composition of functions. The list of parameters must take a variable number of functions, and creating a function applies them from right to left.

(= [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"))

Here __should be replaced by a solution.

You should not use a function in this task comp.


I found a solution:

(fn [& xs]
  (fn [& ys]
    (reduce #(%2 %1)
            (apply (last xs) ys) (rest (reverse xs)))))

It works. But I do not quite understand how it works reduce. How does he represent (apply f_1 (apply f_2 ...(apply f_n-1 (apply f_n args))...)?

+4
source share
4 answers

3 . , . , , .

-,

(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))))

next ,

(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))))))))
+8

( ) 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 atop chain , reduce .

(defn comp [& fs]
  (reduce chain identity fs))

, . ..

, :

((comp #(.toUpperCase %) #(apply str %) take) 5 "hello world") ;=> "HELLO"

, chain (no reduce):

((chain identity
        (chain (chain #(.toUpperCase %)
                      #(apply str %))
               take))
 5 "hello world")
;=> "HELLO"

reduce . ( , Clojure):

def reduce(f, init, seq):
    result = init
    for item in seq:
        result = f(result, item)
    return result

. , reduce , , , , (, , , ).

+5

:

(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

f <--- #(.toUpperCase %)
g <--- #(apply str %)

.

#(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

fs , .

5 " "

:

  • 5 "", (\ h\e\l\l\o)
  • str, ""
  • toUppercase "HELLO"
+5

:

(def c (comp f1 ... fn-1 fn))

(c p1 p2 ... pm)

c :

  • comp fn p*;

  • fn-1 ;

    (...)

  • f1 ,

.

  • first, the rightmost parameter (last xs)is applied to the parameters ys:

    (apply (last xs) ys)
    
  • other parameters change to reduce:

    (rest (reverse xs))
    
  • reduce accepts the provided initial result and a list of functions and iteratively applies the functions to the result:

    (reduce #(%2 %1) ..init.. ..functions..)
    
0
source

Source: https://habr.com/ru/post/1523828/


All Articles