Prograamming Clojure筆記の4——関数式プログラミング
関数プログラミングのいくつかの概念
pure functions
pure functionはside effectsがない関数です.パラメータに依存しない他、戻り値以外は影響しません.
Persistent Data Structures
データ構造が可変でないことは、Clojureが関数式プログラミングと同時進行を実現する鍵である.しかし、データが可変でないということは、変更時にコピーが必要であり、性能を保証するために、Clojureはデータを共有します.
Laziness and Recursion
Clojureでは関数と表現は不活性ではなく,シーケンスは一般に不活性である.
Referential Transparency
どのように惰性になりますか
簡単な再帰
最後の再帰的は、関数が返される表式で再帰的に行われます.上の関数が後戻りではないのは、再帰後に足し算があるからです.最後の再帰については、Clojureは、テール・call optimization(TCO)、すなわち、いわゆる最終再帰最適化を行い、再帰的に反復に変換することができる.
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