Prograamming Clojure筆記の4——関数式プログラミング


関数プログラミングのいくつかの概念
pure functions
pure functionはside effectsがない関数です.パラメータに依存しない他、戻り値以外は影響しません.
Persistent Data Structures
データ構造が可変でないことは、Clojureが関数式プログラミングと同時進行を実現する鍵である.しかし、データが可変でないということは、変更時にコピーが必要であり、性能を保証するために、Clojureはデータを共有します.
Laziness and Recursion
Clojureでは関数と表現は不活性ではなく,シーケンスは一般に不活性である.
Referential Transparency
どのように惰性になりますか
簡単な再帰
;          
(defn stack-consuming-fibo [n] (cond (= n 0) 0 (= n 1) 1 :else (+ (stack-consuming-fibo (- n 1)) (stack-consuming-fibo (- n 2)))))
最終再帰(tail recursion)
最後の再帰的は、関数が返される表式で再帰的に行われます.上の関数が後戻りではないのは、再帰後に足し算があるからです.最後の再帰については、Clojureは、テール・call optimization(TCO)、すなわち、いわゆる最終再帰最適化を行い、再帰的に反復に変換することができる.
(defn tail-fibo [n] (letfn [(fib [current next n] (if (zero? n) current (fib next (+ current next) (dec n))))] (fib 0N 1N n)))
;  letfn       ,                  letfn     。
;        ,  JVM     TCO,        。
recurを使ってself-recursionを行います.
;  recur      
(defn tail-fibo [n] (letfn [(fib [current next n] (if (zero? n) current (recur next (+ current next) (dec n))))] (fib 0N 1N n)))
不活性シーケンス
(defn lazy-seq-fibo
  ([]
    (concat [0 1] (lazy-seq-fibo 0N 1N)))
  ([a b]
    (let [n (+ a b)]
      (lazy-seq 
        (cons n (lazy-seq-fibo b n))))))

;      lazy-seq ,  lazy-seq-fibo           ,         。

;             lazy-seq,          。
;  ,  interate          。
(take 5 (iterate (fn [[a b]] [b (+ a b)]) [0 1]))
-> ([0 1] [1 1] [1 2] [2 3] [3 5])

(defn fibo []
  (map first (iterate (fn [[a b]] [b (+ a b)]) [0N 1N])))
ヘッダ要素を引用しない
;            
(def head-fibo (lazy-cat [0N 1N] (map + head-fibo (rest head-fibo))))
Lazer Than Lazy(なるべくシーケンスライブラリを使用)
;    :h :t:h 。  loop recur   :
(defn count-heads-pairs [coll]
  (loop [cnt 0 coll coll]
    (if (empty? coll)
      cnt
      (recur (if (= :h (first coll) (second coll))
              (inc cnt)
              cnt)
             (rest coll)))))
;                  
;          
(defn by-pairs [coll]
  (let [take-pair (fn [c]
                      (when (next c) (take 2 c)))]
       (lazy-seq
         (when-let [pair (seq (take-pair coll))]
           (cons pair (by-pairs (rest coll)))))))
;       :h        
(defn count-heads-pairs [coll]
  (count (filter (fn [pair] (every? #(= :h %) pair))
                 (by-pairs coll))))

;          partition      by-pairs
;(partition size step? coll)
(partition 2 1 [:h :t :t :h :h :h])
-> ((:h :t) (:t :t) (:t :h) (:h :h) (:h :h))

;   count/filter   ,                  。comp  (compose)         。
(def ^{:doc "Count items matching a filter"}
    count-if (comp count filter))

;         cout-heads-pairs       
(defn count-runs
  "Count runs of length n where pred is true in coll."
  [n pred coll]
  (count-if #(every? pred %) (partition n 1 coll)))
;  
(count-runs 2 #(= % :h) [:h :t :t :h :h :h])
-> 2
partialアプリ
;        count-heads-pairs  ,    partial  count-runs    
(def ^{:doc "Count runs of length two that are both heads"} count-heads-pairs (partial count-runs 2 #(= % :h)))

;partial