SlideShare a Scribd company logo
Clojure
Programming
        reading (※お菓子会)
(CHAPTER 2. Functional Programming)
           2012/07/28 @ponkore



                    1
自己紹介
•   @ponkore
•   職業:とあるSIerのSE

•   業務では最近コード書いてません(どちらかというと管理する側)

    •   Excel方眼紙でドキュメント、Excel&VBAで進 ・品質管理... orz

    •   Java, C#,VB.Net とかが多い

•   Lispやりてぇ→何か無いかな→Java, Lispでぐぐる

    →Clojure発見(2011年秋)

    →2012/04/22 kyoto.clj 顔出し(この時は聞くだけでした...)

    → 今日はなんとか発表(今ここ)


                                2
はじめに
•   大きな「節」単位で整理してみました。

•   それなりに端折ります(細かいところまで完全には読み込
    めていません)。

•   本の中で出てきているコードはなるべく引用しています。

    •   Preface|xvii ページ “Using Code Examples”によると作者に
        contactをする必要もなさそうなので。

•   ツッコミ、誤りのご指摘、補足は大歓迎です。


                           3
Chapter 2. Functional Programming

 •   この章で書いてある内容(ざっくり)

     •   (Immutable な)値の重要性

     •   高階関数(Higher Order Function:HOF)

     •   関数の部分適用

     •   関数の合成(Composition)

     •   Logging System を作ってみよう

     •   純関数(Pure Functions)とメモ化とか


                           4
What does Functional
Programming Mean?
•   「関数型プログラミング」→いろんな言語
    で定義はまちまち(少しあいまいな概念)

•   Clojure 的には...

    -   immutableなデータ構造

    -   高階関数(higher-order-functions)

    -   関数合成、等々

                        5
On the Importance of
Values(1)
• About Values
   - JVM標準のBoolean、Number、
    Character、String、といった型は、
    Clojureでは immutable な値として利用
    できるようにしている(安心して使っ
    てよし!)

               6
On the Importance of
Values(2)
  • Clojure では以下の比較式はすべてtrue
        (= 5 5)
        (= 5 (+ 2 3))
        (= "boot" (str "bo" "ot"))               ;   下記Memo 参照


        (= nil nil)
        (let [a 5]
         (do-something-with-a-number a)
         (= a 5))

Memo: Clojure における ‘=’ は、imutable な’値’に対して比較。javaの == とはちょっと違う。
clojure.core/=
([x] [x y] [x y & more])
  Equality. Returns true if x equals y, false if not. Same as
  Java x.equals(y) except it also works for nil, and compares
  numbers and collections in a type-independent manner. Clojure's immutable data
  structures define equals() (and thus =) as a value, not an identity,
  comparison.

                                         7
On the Importance of
Values(3)
 • Comparing Values to Mutable Objects
        Mutable Object の例(StatefulInteger class)
public class StatefulInteger extends Number {
 private int state;
 public StatefulInteger (int initialState) {
  this.state = initialState; }
 public void setInt (int newState) { this.state = newState; }
 public int intValue () { return state; }
 public int hashCode () { return state; }
 public boolean equals (Object obj) {
    return obj instanceof StatefulInteger &&
     state == ((StatefulInteger)obj).state;
 }}




                               8
On the Importance of
Values(4)
•   (ちょっと横道)

    StatefulInteger クラスを Java で書くと、クラスパスを通し
    たりなにかとめんどい。ので、Clojure で書いてみた。
    ;;; gist に上げてあります       https://gist.github.com/3162439
    (defprotocol IStatefulInteger
      (setInt [this new-state])
      (intValue [this]))

    (deftype StatefulInteger [^{:volatile-mutable true} state]
      IStatefulInteger
      (setInt [this new-state] (set! state new-state))
      (intValue [this] state)
      Object
      (hashCode [this] state)
      (equals [this obj]
        (and (instance? (class this) obj)
             (= state (.intValue obj)))))
                                    9
On the Importance of
  Values(5)
   • StatefulInteger をClojure からいじってみる
(def five (StatefulInteger. 5))
;= #'user/five
(def six (StatefulInteger. 6))
;= #'user/six
(.intValue five)
;= 5
(= five six)                      fiveは“5”のつもり
;= false                           なのに“6”に
(.setInt five 6)
                                  なってしまった
;= nil
(= five six)
;= true



                             10
On the Importance of
 Values(6)
   • StatefulInteger をさらに邪悪にいじる
(defn print-number           ※前ページの続き:five=6, six=6
 [n]
 (println (.intValue n))
 (.setInt n 42))           ; printlnした後に値を42に変更!
;= #'user/print-number
(print-number six)
; 6
;= nil
(= five six)
;= false     ※前ページの結果はtrue→状態が変わった!

(= five (StatefulInteger. 42)) ;P.56の例ではfiveだがsixの誤りでは?
;= true


                             11
On the Importance of
Values(7)
• Ruby は String でさえ Mutable
  >>   s= "hello"
  =>   "hello"
  >>   s << "*"
  =>   "hello*"
  >>   s == "hello"
  =>   false
 ただし.freezeを使えば不用意な変更は阻止できる
  >> s.freeze
  => "hello*"
  >> s << "*"
  RuntimeError: can't modify frozen String
      from (irb):4
      from /opt/local/bin/irb:12:in `<main>'


                         12
On the Importance of
Values(8)
• Ruby は Hash の key でさえ変更可能
 >>   h = {[1, 2] => 3} # [1,2]というキーをもつHash hを作る
 =>   {[1, 2]=>3}
 >>   h[[1,2]]
 =>   3
 >>   h.keys
 =>   [[1, 2]]
 >>   h.keys[0]
 =>   [1, 2]
 >>   h.keys[0] << 3
 =>   [1, 2, 3]      # h.keys[0]はもともと[1,2]だった

 >>   h[[1,2]]       # [1,2]にhitする値はもうhには無い

 =>   nil            ※h.keys[0].freeze で keys[0]を保護できなくはない




                             13
On the Importance of
Values(9)
• Clojureのmapのkeyは変更不可
 (def h {[1, 2] 3})
 ;= #'user/h
 (h [1 2])
 ;= 3
 (conj (first (keys h)) 3)
 ;= [1 2 3]                  ※h のkey [1 2] が変更
 (h [1 2])                   されたわけではない
 ;= 3              元のまま
 h
 ;= {[1 2] 3}




                        14
On the Importance of
Values(10)
•       A Critical Choice
    •    “自由に変更可能な状態を持つ”(Mutable な)オブジェクトを使
         えてしまうと...

        ◦ Mutableなオブジェクトは、安全にメソッドに渡せない
        ◦ Mutableなオブジェクトは、hashのkeyやsetのentryとかには安心して使え
         ない
        ◦ Mutableなオブジェクトは、安全にキャッシュできない(キーが変更されうる)
        ◦ Mutableなオブジェクトは、マルチスレッド環境では安心して使えない(ス
         レッド間で正しく同期させる必要がある)

        ◦ 色々理由はあるけど、今後のことを考えてMutableなオブジェクトはなるべ
         く使わない方向にしたよ、ということ。

                             15
First-Class and Higher
Order-Functions(1)
  • 関数型プログラミングの特徴(要件)
    ◦ 関数自身を値として他のデータと同様に取り扱える
     =関数を引数や戻り値として取り扱える


  • 例:call_twice
# Ruby                              # Python
def call_twice(x, &f)               def call_twice(f, x):
  f.call(x)                           f(x)
  f.call(x)                           f(x)
end
                                    call_twice(print, 123)
call_twice(123) {|x| puts x}

                               16
First-Class and Higher
Order-Functions(2)
• Clojure では...   まあ普通な感じ




        ;Clojure
        (defn call-twice [f x]
          (f x)
          (f x))

        (call-twice println 123)




                     17
First-Class and Higher
    Order-Functions(3)
      •   map (clojure.core/map)

(map clojure.string/lower-case [“Java” “Imperative” “Weeping”
                                “Clojure” “Learning” “Peace”])
;= (“java” “imperative” “weeping” “clojure” “learning” “peace”)


(map * [1 2 3 4] [5 6 7 8])   ;collection が複数ある場合
;= (5 12 21 32)
;中身としては (map #(* % %2) [1 2 3 4] [5 6 7 8])

(map * [1 2 3] [4 5 6 7] [8 9 10 11 12])
;= (32 90 180)    ;※要素の数は少ない方に合わせられる
;中身としては (map #(* % %2 %3) [1 2 3] [4 5 6 7] [8 9 10 11 12])


                                18
First-Class and Higher
Order-Functions(4)
•   reduce (clojure.core/reduce)
      (reduce max [0 -3 10 48])

          (max 0 -3)
          ;= 0
          (max 0 10)
          ;= 10
          (max 10 48)
          ;= 48
      ;= 48     (のはず P.63   には 10と書いてある...)


      (max (max (max 0 -3) 10) 48)
      ;= 48

      ;あるいは以下のようも書ける
      (reduce #(max % %2) [0 -3 10 48])
                                19
First-Class and Higher
   Order-Functions(5)
   •
(reduce
       reduce (clojure.core/reduce) 続き
 (fn [m v]                              ; (fn []... ) anonymous function
    (assoc m v (* v v)))
 {}
 [1 2 3 4])
 ;(assoc   {}   1 (* 1 1))              =>   {1   1}
 ;(assoc   {1   1} 2 (* 2 2))           =>   {2   4,1 1}
 ;(assoc   {2   4,1 1} 3 (* 3 3))       =>   {3   9,1 1,2 4}
 ;(assoc   {3   9,1 1,2 4} 4 (* 4 4))   =>   {4   16,1 1,2 4,3 9}
;= {4 16, 3 9, 2 4, 1 1}

;あるいは以下のようも書ける
(reduce
 #(assoc % %2 (* %2 %2)))               ; #(...) function literal
 {}
 [1 2 3 4])

                                                  20
First-Class and Higher
    Order-Functions(6)
    •   Applying Ourselves Partially (関数部分適用の話)
(def only-strings (partial filter string?))
;= #’user/only-strings

(only-strings [“a” 5 “b” 6])
;= (“a” “b”)

;ところで only-strings の正体って何?
only-strings
;= #<core$partial$fn__3794   clojure.core$partial$fn__3794@5719510f>
;関数っぽい

(class only-strings)
;= user$only_strings         ;だそうで...




                                         21
First-Class and Higher
     Order-Functions(7)
     •   Applying Ourselves Partially (関数部分適用の話)
partial versus function literals.

 ; only-strings 相当のことを関数リテラルでやってみる
 (#(filter string? %) [“a” 5 “b” 6])
 ;= (“a” “b”)     ; できた!簡単!

 ;filter の述語を置き換える、なんてことも...
 (#(filter % [“a” 5 “b” 6]) string?)
 ;= (“a” “b”)
 (#(filter % [“a” 5 “b” 6]) number?)
 ;= (5 6)

 ;partial じゃなく関数リテラルだけでもいいんじゃ?....
 ; => partial の場合、引数を厳密に指定しなくてもよい場合 にすっきり見える
     (次ページ)                     22
First-Class and Higher
  Order-Functions(8)
   •   Applying Ourselves Partially (関数部分適用の話)
partial versus function literals. (続き)
 ;まずは関数リテラル
 (#(map *) [1 2 3] [4 5 6] [7 8 9])
 ;= ArityException Wrong number of args (3) passed ...
 (#(map * % %2 %3) [1 2 3] [4 5 6] [7 8 9])
 ;= (28 80 162)
 (#(map * % %2 %3) [1 2 3] [4 5 6])
 ;= ArityException Wrong number of args (2) passed ...
 (#(apply map * %&) [1 2 3] [4 5 6] [7 8 9])
 ;= (28 80 162)
 (#(apply map * %&) [1 2 3])
 ;= (1 2 3)      ; %& で引数の数は気にしなくて良くなったが apply           がうっとおしい...
 ;次に partial の出番
 ((partial map *) [1 2 3] [4 5 6] [7 8 9])
 ;= (28 80 162)
 ((partial map *) [1 2 3])
 ;= (1 2 3)                   23
Composition of
Function(ality)(1)
•   Composition って何?
    (defn negated-sum-str
      [& numbers]                                           10    12     3.4
      (str (- (apply + numbers))))
    ;= #’user/negated-num-str
    (negated-sum-str 10 12 3.4)                                    +
    ;= “-25.4”
                                                                 25.4
    (def negated-sum-str (comp str - +))                           -
    ;= #’user/negated-num-str
    ;※関数を返してくれる!                                                 -25.4
    ;しかもpartial同様引数の個数とか気にしない!
                                                                  str
    (negated-sum-str 10 12 3.4)
    ;= “-25.4”
                                                             “-25.4”
     ※P71.脚注26 “point-free   style” (引数を明示的に指定・参照しないスタイル)

                                          24
Composition of
Function(ality)(2)
•   Composition もう一つの例(camel->keyword)
(require '[clojure.string :as str])
(def camel->keyword (comp keyword
                          str/join
                          (partial interpose -)
                          (partial map str/lower-case)
                          #(str/split % #"(?<=[a-z])(?=[A-Z])")))

;= #'user/camel->keyword
(camel->keyword "CamelCase")
;= :camel-case
(camel->keyword "lowerCamelCase")
;= :lower-camel-case


;; 上記場面では、以下のように ->> を使うこともできる(こっちのほうが多いかな?)
;; ※comp とは順番が異なることに注意。またcompで必要だった partial   等も不要。
(defn camel->keyword
  [s]
  (->> (str/split % #"(?<=[a-z])(?=[A-Z])")
        (map str/lower-case)
        (interpose -)
        str/join
        keyword))                    25
Composition of
 Function(ality)(3)
  •   camel->keywordを応用してみる(camel-pairs->map)
(def camel-pairs->map (comp (partial apply hash-map)
                            (partial map-indexed (fn [i x]
                                                   (if (odd? i)
                                                     x
                                                     (camel->keyword x))))))

;= #'user/camel-pairs->map

(camel-pairs->map ["CamelCase" 5 "lowerCamelCase" 3])
;= {:camel-case 5, :lower-camel-case 3}




                                      26
Composition of
 Function(ality)(4)
 •   Writing Higher-Order Functions
;お約束の adder
(defn adder                  ; 「関数」を戻り値として返す

  [n]
  (fn [x] (+ n x)))
;= #'user/adder
((adder 5) 18)
;= 23
(defn doubler                ; 関数をパラメータとして渡す

  [f]
  (fn [& args]
    (* 2 (apply f args))))
;= #'user/doubler
(def double-+ (doubler +))
;= #'user/doubler-+
(double-+ 1 2 3)             ; Clojureの+はいくつでも引数を取れる。便利。

;= 12                         27
Composition of
 Function(ality)(5)
  •     Building a Primitive Logging System with Composable Higher-Order
        Functions
       •     高階関数を使ってログ出力をいろいろ作ってみる。

      print-logger          パラメータwriterを出力先に指定できる関数を返す。
      *out-logger*          print-logger をつかった、*out* に出力する関数。
      retained-logger       print-logger をつかった、StringWriter に出力する関数。
      file-logger            パラメータfileを出力先に指定できる関数を返す。
      log->file              “message.log”というファイルに出力する関数。
      multi-logger          logger関数を複数順番に呼び出す(doseq)関数を返す。
      log                   multi-loggerを使って、*out*と”message.log”に出力する関数。
      timestamped-logger    ログメッセージに日時を付加する関数を返す。
      log-timestamped       今までの集大成。

コードは長いのでスライド上は省略します。gist に貼ってありますので、そちらをご参照願います。
https://gist.github.com/3173902/

                                         28
Pure Functions
•   Pure Functionって何?

    •   「副作用」(side effects)のない関数、のこと

    •   じゃあ「副作用のある関数」って何?

        1. ランダムな状態に依存(乱数を使ってる、とか)

        2. 1回めの呼び出しと次回以降の呼び出しで結果が変わる

         ※I/Oを実行する関数は副作用あり。

         (P.77 では @ClojureBook のフォロワー数の出力を例示

          →呼び出したタイミングにより値が変わる)


                         29
Pure Functions
•   Why Are Pure Functions Interesting?
    •   Pure functions are easier to reason about.
        •   入力パラメータが決まれば出力は必ず同じ出力になる。

    •   Pure functions are easier to test.

        •   (関数を実行する時点の)状態を考える必要がないのでテストしやすい。

    •   Pure functions are cacheable and trivial to parallelize.

        •   入力パラメータが決まれば出力は必ず同じー>入力パラメータに対応し
            た出力結果をキャッシュ(メモ化)することで、2回目以降の呼び出しを
            高速化できる。

            (次ページにメモ化の例)




                                             30
Pure Functions
       •   メモ化の例(素数判定)
(defn prime?
  [n]
  (cond
   (== 1 n) false
   (== 2 n) true
   (even? n) false
   :else (->> (range 3 (inc (Math/sqrt n)) 2)
              (filter #(zero? (rem n %)))
              empty?)))
(time (prime? 1125899906842679))
; "Elapsed time: 2181.014 msecs"
;= true

(let [m-prime? (memoize prime?)]   ; memoize:「関数をメモ化した関数」を返す。
  (time (m-prime? 1125899906842679))
  (time (m-prime? 1125899906842679)))
; "Elapsed time: 2085.029 msecs"
; "Elapsed time: 0.042 msecs"

                                   31
Functional Programming
in the Real World
       Functional
      Programming
         in the
       Real World



                    メリット
                    •予測しやすいコード
                    •テストの容易さ
                    •再利用の容易さ

             32
ご清聴ありがとうございました。




       33

More Related Content

Clojure programming-chapter-2

  • 1. Clojure Programming reading (※お菓子会) (CHAPTER 2. Functional Programming) 2012/07/28 @ponkore 1
  • 2. 自己紹介 • @ponkore • 職業:とあるSIerのSE • 業務では最近コード書いてません(どちらかというと管理する側) • Excel方眼紙でドキュメント、Excel&VBAで進 ・品質管理... orz • Java, C#,VB.Net とかが多い • Lispやりてぇ→何か無いかな→Java, Lispでぐぐる →Clojure発見(2011年秋) →2012/04/22 kyoto.clj 顔出し(この時は聞くだけでした...) → 今日はなんとか発表(今ここ) 2
  • 3. はじめに • 大きな「節」単位で整理してみました。 • それなりに端折ります(細かいところまで完全には読み込 めていません)。 • 本の中で出てきているコードはなるべく引用しています。 • Preface|xvii ページ “Using Code Examples”によると作者に contactをする必要もなさそうなので。 • ツッコミ、誤りのご指摘、補足は大歓迎です。 3
  • 4. Chapter 2. Functional Programming • この章で書いてある内容(ざっくり) • (Immutable な)値の重要性 • 高階関数(Higher Order Function:HOF) • 関数の部分適用 • 関数の合成(Composition) • Logging System を作ってみよう • 純関数(Pure Functions)とメモ化とか 4
  • 5. What does Functional Programming Mean? • 「関数型プログラミング」→いろんな言語 で定義はまちまち(少しあいまいな概念) • Clojure 的には... - immutableなデータ構造 - 高階関数(higher-order-functions) - 関数合成、等々 5
  • 6. On the Importance of Values(1) • About Values - JVM標準のBoolean、Number、 Character、String、といった型は、 Clojureでは immutable な値として利用 できるようにしている(安心して使っ てよし!) 6
  • 7. On the Importance of Values(2) • Clojure では以下の比較式はすべてtrue (= 5 5) (= 5 (+ 2 3)) (= "boot" (str "bo" "ot")) ; 下記Memo 参照 (= nil nil) (let [a 5] (do-something-with-a-number a) (= a 5)) Memo: Clojure における ‘=’ は、imutable な’値’に対して比較。javaの == とはちょっと違う。 clojure.core/= ([x] [x y] [x y & more]) Equality. Returns true if x equals y, false if not. Same as Java x.equals(y) except it also works for nil, and compares numbers and collections in a type-independent manner. Clojure's immutable data structures define equals() (and thus =) as a value, not an identity, comparison. 7
  • 8. On the Importance of Values(3) • Comparing Values to Mutable Objects Mutable Object の例(StatefulInteger class) public class StatefulInteger extends Number { private int state; public StatefulInteger (int initialState) { this.state = initialState; } public void setInt (int newState) { this.state = newState; } public int intValue () { return state; } public int hashCode () { return state; } public boolean equals (Object obj) { return obj instanceof StatefulInteger && state == ((StatefulInteger)obj).state; }} 8
  • 9. On the Importance of Values(4) • (ちょっと横道) StatefulInteger クラスを Java で書くと、クラスパスを通し たりなにかとめんどい。ので、Clojure で書いてみた。 ;;; gist に上げてあります https://gist.github.com/3162439 (defprotocol IStatefulInteger   (setInt [this new-state])   (intValue [this])) (deftype StatefulInteger [^{:volatile-mutable true} state]   IStatefulInteger   (setInt [this new-state] (set! state new-state))   (intValue [this] state)   Object   (hashCode [this] state)   (equals [this obj]     (and (instance? (class this) obj)          (= state (.intValue obj))))) 9
  • 10. On the Importance of Values(5) • StatefulInteger をClojure からいじってみる (def five (StatefulInteger. 5)) ;= #'user/five (def six (StatefulInteger. 6)) ;= #'user/six (.intValue five) ;= 5 (= five six) fiveは“5”のつもり ;= false なのに“6”に (.setInt five 6) なってしまった ;= nil (= five six) ;= true 10
  • 11. On the Importance of Values(6) • StatefulInteger をさらに邪悪にいじる (defn print-number ※前ページの続き:five=6, six=6 [n] (println (.intValue n)) (.setInt n 42)) ; printlnした後に値を42に変更! ;= #'user/print-number (print-number six) ; 6 ;= nil (= five six) ;= false ※前ページの結果はtrue→状態が変わった! (= five (StatefulInteger. 42)) ;P.56の例ではfiveだがsixの誤りでは? ;= true 11
  • 12. On the Importance of Values(7) • Ruby は String でさえ Mutable >> s= "hello" => "hello" >> s << "*" => "hello*" >> s == "hello" => false ただし.freezeを使えば不用意な変更は阻止できる >> s.freeze => "hello*" >> s << "*" RuntimeError: can't modify frozen String from (irb):4 from /opt/local/bin/irb:12:in `<main>' 12
  • 13. On the Importance of Values(8) • Ruby は Hash の key でさえ変更可能 >> h = {[1, 2] => 3} # [1,2]というキーをもつHash hを作る => {[1, 2]=>3} >> h[[1,2]] => 3 >> h.keys => [[1, 2]] >> h.keys[0] => [1, 2] >> h.keys[0] << 3 => [1, 2, 3] # h.keys[0]はもともと[1,2]だった >> h[[1,2]] # [1,2]にhitする値はもうhには無い => nil ※h.keys[0].freeze で keys[0]を保護できなくはない 13
  • 14. On the Importance of Values(9) • Clojureのmapのkeyは変更不可 (def h {[1, 2] 3}) ;= #'user/h (h [1 2]) ;= 3 (conj (first (keys h)) 3) ;= [1 2 3] ※h のkey [1 2] が変更 (h [1 2]) されたわけではない ;= 3 元のまま h ;= {[1 2] 3} 14
  • 15. On the Importance of Values(10) • A Critical Choice • “自由に変更可能な状態を持つ”(Mutable な)オブジェクトを使 えてしまうと... ◦ Mutableなオブジェクトは、安全にメソッドに渡せない ◦ Mutableなオブジェクトは、hashのkeyやsetのentryとかには安心して使え ない ◦ Mutableなオブジェクトは、安全にキャッシュできない(キーが変更されうる) ◦ Mutableなオブジェクトは、マルチスレッド環境では安心して使えない(ス レッド間で正しく同期させる必要がある) ◦ 色々理由はあるけど、今後のことを考えてMutableなオブジェクトはなるべ く使わない方向にしたよ、ということ。 15
  • 16. First-Class and Higher Order-Functions(1) • 関数型プログラミングの特徴(要件) ◦ 関数自身を値として他のデータと同様に取り扱える =関数を引数や戻り値として取り扱える • 例:call_twice # Ruby # Python def call_twice(x, &f) def call_twice(f, x): f.call(x) f(x) f.call(x) f(x) end call_twice(print, 123) call_twice(123) {|x| puts x} 16
  • 17. First-Class and Higher Order-Functions(2) • Clojure では... まあ普通な感じ ;Clojure (defn call-twice [f x] (f x) (f x)) (call-twice println 123) 17
  • 18. First-Class and Higher Order-Functions(3) • map (clojure.core/map) (map clojure.string/lower-case [“Java” “Imperative” “Weeping” “Clojure” “Learning” “Peace”]) ;= (“java” “imperative” “weeping” “clojure” “learning” “peace”) (map * [1 2 3 4] [5 6 7 8]) ;collection が複数ある場合 ;= (5 12 21 32) ;中身としては (map #(* % %2) [1 2 3 4] [5 6 7 8]) (map * [1 2 3] [4 5 6 7] [8 9 10 11 12]) ;= (32 90 180) ;※要素の数は少ない方に合わせられる ;中身としては (map #(* % %2 %3) [1 2 3] [4 5 6 7] [8 9 10 11 12]) 18
  • 19. First-Class and Higher Order-Functions(4) • reduce (clojure.core/reduce) (reduce max [0 -3 10 48]) (max 0 -3) ;= 0 (max 0 10) ;= 10 (max 10 48) ;= 48 ;= 48 (のはず P.63 には 10と書いてある...) (max (max (max 0 -3) 10) 48) ;= 48 ;あるいは以下のようも書ける (reduce #(max % %2) [0 -3 10 48]) 19
  • 20. First-Class and Higher Order-Functions(5) • (reduce reduce (clojure.core/reduce) 続き (fn [m v] ; (fn []... ) anonymous function (assoc m v (* v v))) {} [1 2 3 4]) ;(assoc {} 1 (* 1 1)) => {1 1} ;(assoc {1 1} 2 (* 2 2)) => {2 4,1 1} ;(assoc {2 4,1 1} 3 (* 3 3)) => {3 9,1 1,2 4} ;(assoc {3 9,1 1,2 4} 4 (* 4 4)) => {4 16,1 1,2 4,3 9} ;= {4 16, 3 9, 2 4, 1 1} ;あるいは以下のようも書ける (reduce #(assoc % %2 (* %2 %2))) ; #(...) function literal {} [1 2 3 4]) 20
  • 21. First-Class and Higher Order-Functions(6) • Applying Ourselves Partially (関数部分適用の話) (def only-strings (partial filter string?)) ;= #’user/only-strings (only-strings [“a” 5 “b” 6]) ;= (“a” “b”) ;ところで only-strings の正体って何? only-strings ;= #<core$partial$fn__3794 clojure.core$partial$fn__3794@5719510f> ;関数っぽい (class only-strings) ;= user$only_strings ;だそうで... 21
  • 22. First-Class and Higher Order-Functions(7) • Applying Ourselves Partially (関数部分適用の話) partial versus function literals. ; only-strings 相当のことを関数リテラルでやってみる (#(filter string? %) [“a” 5 “b” 6]) ;= (“a” “b”) ; できた!簡単! ;filter の述語を置き換える、なんてことも... (#(filter % [“a” 5 “b” 6]) string?) ;= (“a” “b”) (#(filter % [“a” 5 “b” 6]) number?) ;= (5 6) ;partial じゃなく関数リテラルだけでもいいんじゃ?.... ; => partial の場合、引数を厳密に指定しなくてもよい場合 にすっきり見える (次ページ) 22
  • 23. First-Class and Higher Order-Functions(8) • Applying Ourselves Partially (関数部分適用の話) partial versus function literals. (続き) ;まずは関数リテラル (#(map *) [1 2 3] [4 5 6] [7 8 9]) ;= ArityException Wrong number of args (3) passed ... (#(map * % %2 %3) [1 2 3] [4 5 6] [7 8 9]) ;= (28 80 162) (#(map * % %2 %3) [1 2 3] [4 5 6]) ;= ArityException Wrong number of args (2) passed ... (#(apply map * %&) [1 2 3] [4 5 6] [7 8 9]) ;= (28 80 162) (#(apply map * %&) [1 2 3]) ;= (1 2 3) ; %& で引数の数は気にしなくて良くなったが apply がうっとおしい... ;次に partial の出番 ((partial map *) [1 2 3] [4 5 6] [7 8 9]) ;= (28 80 162) ((partial map *) [1 2 3]) ;= (1 2 3) 23
  • 24. Composition of Function(ality)(1) • Composition って何? (defn negated-sum-str [& numbers] 10 12 3.4 (str (- (apply + numbers)))) ;= #’user/negated-num-str (negated-sum-str 10 12 3.4) + ;= “-25.4” 25.4 (def negated-sum-str (comp str - +)) - ;= #’user/negated-num-str ;※関数を返してくれる! -25.4 ;しかもpartial同様引数の個数とか気にしない! str (negated-sum-str 10 12 3.4) ;= “-25.4” “-25.4” ※P71.脚注26 “point-free style” (引数を明示的に指定・参照しないスタイル) 24
  • 25. Composition of Function(ality)(2) • Composition もう一つの例(camel->keyword) (require '[clojure.string :as str]) (def camel->keyword (comp keyword str/join (partial interpose -) (partial map str/lower-case) #(str/split % #"(?<=[a-z])(?=[A-Z])"))) ;= #'user/camel->keyword (camel->keyword "CamelCase") ;= :camel-case (camel->keyword "lowerCamelCase") ;= :lower-camel-case ;; 上記場面では、以下のように ->> を使うこともできる(こっちのほうが多いかな?) ;; ※comp とは順番が異なることに注意。またcompで必要だった partial 等も不要。 (defn camel->keyword [s] (->> (str/split % #"(?<=[a-z])(?=[A-Z])") (map str/lower-case) (interpose -) str/join keyword)) 25
  • 26. Composition of Function(ality)(3) • camel->keywordを応用してみる(camel-pairs->map) (def camel-pairs->map (comp (partial apply hash-map) (partial map-indexed (fn [i x] (if (odd? i) x (camel->keyword x)))))) ;= #'user/camel-pairs->map (camel-pairs->map ["CamelCase" 5 "lowerCamelCase" 3]) ;= {:camel-case 5, :lower-camel-case 3} 26
  • 27. Composition of Function(ality)(4) • Writing Higher-Order Functions ;お約束の adder (defn adder ; 「関数」を戻り値として返す [n] (fn [x] (+ n x))) ;= #'user/adder ((adder 5) 18) ;= 23 (defn doubler ; 関数をパラメータとして渡す [f] (fn [& args] (* 2 (apply f args)))) ;= #'user/doubler (def double-+ (doubler +)) ;= #'user/doubler-+ (double-+ 1 2 3) ; Clojureの+はいくつでも引数を取れる。便利。 ;= 12 27
  • 28. Composition of Function(ality)(5) • Building a Primitive Logging System with Composable Higher-Order Functions • 高階関数を使ってログ出力をいろいろ作ってみる。 print-logger パラメータwriterを出力先に指定できる関数を返す。 *out-logger* print-logger をつかった、*out* に出力する関数。 retained-logger print-logger をつかった、StringWriter に出力する関数。 file-logger パラメータfileを出力先に指定できる関数を返す。 log->file “message.log”というファイルに出力する関数。 multi-logger logger関数を複数順番に呼び出す(doseq)関数を返す。 log multi-loggerを使って、*out*と”message.log”に出力する関数。 timestamped-logger ログメッセージに日時を付加する関数を返す。 log-timestamped 今までの集大成。 コードは長いのでスライド上は省略します。gist に貼ってありますので、そちらをご参照願います。 https://gist.github.com/3173902/ 28
  • 29. Pure Functions • Pure Functionって何? • 「副作用」(side effects)のない関数、のこと • じゃあ「副作用のある関数」って何? 1. ランダムな状態に依存(乱数を使ってる、とか) 2. 1回めの呼び出しと次回以降の呼び出しで結果が変わる ※I/Oを実行する関数は副作用あり。 (P.77 では @ClojureBook のフォロワー数の出力を例示 →呼び出したタイミングにより値が変わる) 29
  • 30. Pure Functions • Why Are Pure Functions Interesting? • Pure functions are easier to reason about. • 入力パラメータが決まれば出力は必ず同じ出力になる。 • Pure functions are easier to test. • (関数を実行する時点の)状態を考える必要がないのでテストしやすい。 • Pure functions are cacheable and trivial to parallelize. • 入力パラメータが決まれば出力は必ず同じー>入力パラメータに対応し た出力結果をキャッシュ(メモ化)することで、2回目以降の呼び出しを 高速化できる。 (次ページにメモ化の例) 30
  • 31. Pure Functions • メモ化の例(素数判定) (defn prime? [n] (cond (== 1 n) false (== 2 n) true (even? n) false :else (->> (range 3 (inc (Math/sqrt n)) 2) (filter #(zero? (rem n %))) empty?))) (time (prime? 1125899906842679)) ; "Elapsed time: 2181.014 msecs" ;= true (let [m-prime? (memoize prime?)] ; memoize:「関数をメモ化した関数」を返す。 (time (m-prime? 1125899906842679)) (time (m-prime? 1125899906842679))) ; "Elapsed time: 2085.029 msecs" ; "Elapsed time: 0.042 msecs" 31
  • 32. Functional Programming in the Real World Functional Programming in the Real World メリット •予測しやすいコード •テストの容易さ •再利用の容易さ 32

Editor's Notes