comp

added
1.0

ns
clojure.core

type
function

(comp) (comp f) (comp f g) (comp f g & fs)

Takes a set of functions and returns a fn that is the composition
of those fns.  The returned fn takes a variable number of args,
applies the rightmost of fns to the args, the next
fn (right-to-left) to the result, etc.

                (def negative-quotient (comp - /))
;; #'user/negative-quotient

(negative-quotient 8 3)  ;;=> -8/3

(def concat-and-reverse (comp (partial apply str) reverse str)) 
;; #'user/concat-and-reverse

(concat-and-reverse "hello" "clojuredocs")
;;=> "scoderujolcolleh"

            
                ((comp str +) 8 8 8)   
;;=> "24"

            
                (map
   (comp - (partial + 3) (partial * 2))
       [1 2 3 4])
;;=>  (-5 -7 -9 -11)
            
                (filter (comp not zero?) [0 1 0 2 0 3 0 4])
;;=> (1 2 3 4)
            
                ;; make a struct 'goods'. it assumes that every goods has
;; its id number and price.
(defstruct goods :id :price)

;; generate data.
(def data (map #(struct goods %1 %2)
\t       (shuffle (range 0 10)) 
               (shuffle
\t          (into (range 100 500 100)
\t\t\t(range 100 500 100)))))

(defn comp-goods-price
  "a compare function by :price of the struct 'goods.' the sort order 
   is that the lower price is superior to the higher one and if the 
   price is same, the lower id is superior to the higher one."
  [el1 el2]
  (if (or  (< (:price el1) (:price el2))
           (and (= (:price el1) (:price el2)) (< (:id el1) (:id el2))))
    true
    false))

;; The shuffle will cause your results to differ.
data   
;;=> ({:id 1, :price 300} {:id 6, :price 100} 
;;    {:id 3, :price 100} {:id 4, :price 400}
;;    {:id 0, :price 300} {:id 2, :price 200} 
;;    {:id 5, :price 200} {:id 8, :price 400})

(sort (comp comp-goods-price) data)
;;=> ({:id 3, :price 100} {:id 6, :price 100} 
;;    {:id 2, :price 200} {:id 5, :price 200} 
;;    {:id 0, :price 300} {:id 1, :price 300}
;;    {:id 4, :price 400} {:id 8, :price 400})

(sort-by :price < data) ; compare this with the above.
;;=> ({:id 6, :price 100} {:id 3, :price 100} 
;;    {:id 2, :price 200} {:id 5, :price 200} 
;;    {:id 1, :price 300} {:id 0, :price 300} 
;;    {:id 4, :price 400} {:id 8, :price 400})

;; Yet another example of 'comp' by PriorityBlockingQueue.

(import [java.util.concurrent PriorityBlockingQueue])
;; java.util.concurrent.PriorityBlockingQueue

(def pqdata (new PriorityBlockingQueue 8
\t\t (comp comp-goods-price)))
;; #'user/pqdata

(doseq [x data] (.add pqdata x))
;;=> nil

(dotimes [_ 8] (println (.poll pqdata)))
;;  {:id 3, :price 100}
;;  {:id 6, :price 100}
;;  {:id 2, :price 200}
;;  {:id 5, :price 200}
;;  {:id 0, :price 300}
;;  {:id 1, :price 300}
;;  {:id 4, :price 400}
;;  {:id 8, :price 400}
;;=> nil

            
                (def countif (comp count filter))
#'user/countif

(countif even? [2 3 1 5 4])
;;=> 2
            
                ; Get 2nd to last element from a list
( (comp second reverse) '("a" 2 7 "b"))   
;;=> 7
            
                ; We need an example that composes more than just two functions.
; The following example is an overly complicated reimplementation of 'nth'
; but it does show the composition of an arbitrary number of functions (rest).
( #((apply comp first (repeat %2 rest)) %1) [1 2 3 4 5 6] 3 ) 
;;=> 4
            
                ; `comp`-ing maps, filters with a little help from our friend `partial`
; the following function filters numbers in a `coll` if it is divisible by 3
; then on that filtered `coll`, multiplies all by 2

; a little helper to find if a number is div by 3 
; also comp-ed

(defn mod3nz? 
  [x]
  ((comp not zero? mod) x 3))

; now for that elusive function that muls by 2 after filter those not div by 3
(defn mul-2-nd-3
  "Takes a seq of numbers, filters those not divisible by 3 and muls them by 2"
  [xs]
  ((comp (partial map #(* % 2))
        (partial filter #(mod3nz? %)))
   xs))

(mul-2-nd-3 [16 15 30 43]) ;; => (32 86)

            
                ; Split a number into sequence of it's digits
((comp (partial map (comp read-string str)) str) 33)
;;=> (3 3)
            
                ;; Keywords are used as functions to access data in maps.

(:foo {:foo "bar"})
;;=> "bar"

;; With a nested data structure, it is common to use
;; several keywords in sequence to navigate the hierarchy.

(def my-data {:this {:that {:the-other "val"}}})
;;=> #'user/my-data

(-> my-data :this :that :the-other)
;;=> "val"

;; Since keywords are functions,
;; they can be 'comp'ed just like any other function.

(def those (comp :the-other :that :this))  ; Note: reverse order
;;=> #'user/those

(those my-data)
;;=> "val"

;; The composed keyword-sequence can be used with other keywords: -

(def my-data-2
  {:this {:that {:the-other {:a "apple" :b "banana"}}}})
;;=> #'user/my-data-2

(let [a (-> my-data-2 those :a)
      b (-> my-data-2 those :b)]
  (str "These: " a ", " b))
;;=> "These: apple, banana"
            
                ;; ((comp func1 func2) data) mean ...
;; (-> data func2 func1)
;; so,
((comp (partial * 3) inc) 1)
;; means
(* 3 (inc 1))

;; advanced ...

((comp seq re-seq) #"(\\w+)=(\\S+)" "foo=bar")
;; (["foo=bar" "foo" "bar"])
(seq (re-seq #"(\\w+)=(\\S+)" "foo=bar"))

;; * "#(\\w+)..." and "foo=..." are arguments for #re-seq
            
                ;; comp is the transducer equivalent to thrush 

;; An example of using the "thread-last" macro to get
;; the sum of the first 10 even squares.
(->> (range)
     (map #(* % %))
     (filter even?)
     (take 10)
     (reduce +))
;;=> 1140

;; Many the seq functions now produce transducers.
;; `reduce` does not but has been replaced with `transduce`.
(transduce 
   (comp
     (map #(* % %))
     (filter even?)
     (take 10))
   + 0 (range) )
;;=> 1140