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

PDF
関数型プログラミング in javascript
PPTX
C# 式木 (Expression Tree) ~ LINQをより深く理解するために ~
PPT
Algorithm 速いアルゴリズムを書くための基礎
PPTX
C# LINQ ~深く知って、使いまくろう~
PPTX
メタプログラミング C#
PDF
LINQソースでGO!
PDF
C++コミュニティーの中心でC++をDISる
PPTX
関数型プログラミング in javascript
C# 式木 (Expression Tree) ~ LINQをより深く理解するために ~
Algorithm 速いアルゴリズムを書くための基礎
C# LINQ ~深く知って、使いまくろう~
メタプログラミング C#
LINQソースでGO!
C++コミュニティーの中心でC++をDISる

What's hot (20)

PDF
Material
PDF
C++0x 言語の未来を語る
PDF
Pfi Seminar 2010 1 7
PPTX
C#を始めたばかりの人へのLINQ to Objects
PDF
ゲーム開発者のための C++11/C++14
PDF
Java電卓勉強会資料
PPTX
C#6.0の新機能紹介
PDF
Java初心者勉強会(2015/08/07)資料
ODP
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
PDF
並行プログラミングと継続モナド
PDF
Bluespec @waseda(PDF)
PDF
Frege, What a Non-strict Language
PDF
PHP5.5新機能「ジェネレータ」初心者入門
PPTX
競技プログラミングのためのC++入門
PDF
Scala の関数型プログラミングを支える技術
PPTX
函数プログラミングの エッセンスと考え方
PDF
クロージャデザインパターン
PDF
Scalaで萌える関数型プログラミング[1.1.RC1]
PDF
C++11概要 ライブラリ編
PDF
ノンプログラマーでも明日から使えるJavaScript簡単プログラム 先生:柳井 政和
Material
C++0x 言語の未来を語る
Pfi Seminar 2010 1 7
C#を始めたばかりの人へのLINQ to Objects
ゲーム開発者のための C++11/C++14
Java電卓勉強会資料
C#6.0の新機能紹介
Java初心者勉強会(2015/08/07)資料
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
並行プログラミングと継続モナド
Bluespec @waseda(PDF)
Frege, What a Non-strict Language
PHP5.5新機能「ジェネレータ」初心者入門
競技プログラミングのためのC++入門
Scala の関数型プログラミングを支える技術
函数プログラミングの エッセンスと考え方
クロージャデザインパターン
Scalaで萌える関数型プログラミング[1.1.RC1]
C++11概要 ライブラリ編
ノンプログラマーでも明日から使えるJavaScript簡単プログラム 先生:柳井 政和
Ad

Viewers also liked (6)

PDF
Practical Clojure Programming
PDF
Clojure Intro
PDF
Clojure A Dynamic Programming Language for the JVM
PPTX
我的奇幻漂流日記
PDF
Functional web development with Git(Hub), Heroku and Clojure
PDF
Clojure - An Introduction for Java Programmers
Practical Clojure Programming
Clojure Intro
Clojure A Dynamic Programming Language for the JVM
我的奇幻漂流日記
Functional web development with Git(Hub), Heroku and Clojure
Clojure - An Introduction for Java Programmers
Ad

Similar to Clojure programming-chapter-2 (20)

PPTX
関数型言語&形式的手法セミナー(3)
PDF
Javaプログラミング入門
PDF
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
PDF
関数型プログラミング入門 with OCaml
PDF
Scalaプログラミング・マニアックス
PDF
F#入門 ~関数プログラミングとは何か~
PDF
たのしい関数型
PDF
関数プログラミング入門
ODP
これから Haskell を書くにあたって
PDF
モナドハンズオン前座
PDF
Scala conf2013
ODP
Javascriptで学ぶ Functional Programming
PDF
プログラミング言語Scala
KEY
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
PPTX
第3回輪講
PDF
BOF1-Scala02.pdf
PDF
BOF1-Scala02.pdf
PDF
BOF1-Scala02.pdf
PDF
Programming in Scala Chapter 17 Collections
PPTX
Introduction to Functional Programming
関数型言語&形式的手法セミナー(3)
Javaプログラミング入門
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
関数型プログラミング入門 with OCaml
Scalaプログラミング・マニアックス
F#入門 ~関数プログラミングとは何か~
たのしい関数型
関数プログラミング入門
これから Haskell を書くにあたって
モナドハンズオン前座
Scala conf2013
Javascriptで学ぶ Functional Programming
プログラミング言語Scala
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
第3回輪講
BOF1-Scala02.pdf
BOF1-Scala02.pdf
BOF1-Scala02.pdf
Programming in Scala Chapter 17 Collections
Introduction to Functional Programming

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