圏論とプログラミング読書会#2 資料

48
けんろんどくしょかい #2 (2014/12/11) 直和集合について / ラムダ計算のさわり / 型なしラムダ計算 数学的厳密性は放棄します @gomi_ningen

Upload: 53ningen

Post on 18-Jul-2015

1.496 views

Category:

Engineering


5 download

TRANSCRIPT

けんろんどくしょかい #2 (2014/12/11)

直和集合について / ラムダ計算のさわり / 型なしラムダ計算

数学的厳密性は放棄します @gomi_ningen

#2-1 前回躓いた直和集合について ちゃんと定義に向き合うのが結局一番はやい気がした

#2-1 前回躓いた直和集合について 直和集合(direct sum) 集合A, Bに属さない 要素 * を考える A* = (A, *) B* = (*, B) とすると 直和集合 A + B = A* ∪ B* つまり A* の各要素 と B* の 各要素すべてを 集めたものが直和集合になります

#2-1 前回躓いた直和集合について 直和集合(direct sum) 集合A, Bに属さない 要素 * を考える A* = (A, *) B* = (*, B) とすると 直和集合 A + B = A* ∪ B* つまり A* の各要素 と B* の 各要素すべてを 集めたものが直和集合になります

なぜこうするのか

#2-1 前回躓いた直和集合について 直和集合(direct sum) 集合A, Bに属さない 要素 * を考える A* = (A, *) B* = (*, B) とすると 直和集合 A + B = A* ∪ B* つまり A* の各要素 と B* の 各要素すべてを 集めたものが直和集合になります

 こうすると  集合  A  の元と  Bの元 を  必ず分離できる  

#2-1 前回躓いた直和集合について 直和集合(direct sum) 集合A, Bに属さない 要素 * を考える A* = (A, *) B* = (*, B) とすると 直和集合 A + B = A* ∪ B* つまり A* の各要素 と B* の 各要素すべてを 集めたものが直和集合になります

集合  Int  と  Int  でも常に  (Int,  *)  !=  (*,  Int)  となる! ここの目的は  とにかく  2つの集合の共通部分をなくすこと  

#2-1 前回躓いた直和集合について 直和集合(direct sum) 集合A, Bに属さない 要素 * を考える A* = (A, *) B* = (*, B) とすると 直和集合 A + B = A* ∪ B* つまり A* の各要素 と B* の 各要素すべてを 集めたものが直和集合になります

集合  Int  と  Int  で考えると  常に  (Int,  *)  !=  (*,  Int)  となる!  こうすると その元はもともと  Aに属していたか,  Bに属していたか必ず分かる!  

#2-1 前回躓いた直和集合について 直和集合(direct sum) 集合A, Bに属さない 要素 * を考える A* = (A, *) B* = (*, B) とすると 直和集合 A + B = A* ∪ B* つまり A* の各要素 と B* の 各要素すべてを 集めたものが直和集合になります

こうしてAに属していたという文脈をつけた集合A*と  Bに属していたという文脈をつけた集合B*のすべての元を集めたものが直和集合  

#2-1 前回躓いた直和集合について 直和集合(direct sum) 集合A, Bに対して A* = A × {1} B* = B × {2} とすると 直和集合 A + B = A* ∪ B* つまり A* の各要素 と B* の 各要素すべてを 集めたものが直和集合になります

だからこうしてもよい  A*  =  {  (a1,  1),  (a2,  1),  …  }  B*  =  {  (b1,  2),  (b2,  2),  …  }    こうすると,たとえ共通部分を持っていようと,必ずA,  B  どちらに所属していたか  区別できますよね?  

#2-1 前回躓いた直和集合について Scalaでの例:Eitherは直和型 def getIntOrStr(needToInt: Boolean): Either[Int, String] = { if(needToInt) Left(100) else Right("hoge")}Either[T,U] には Letf[T], Right[U] のどちらかのインスタンスが入る集合 Either[T,U] の要素は集合 Left T の要素すべてと,集合 Right U の要素すべて

#2-1 前回躓いた直和集合について Scalaでの例:Eitherは直和型 集合 Either[Int, Int] の要素は集合 Left[Int] の要素すべてと 集合 Right[Int] の要素すべてを集めたものLeft[Int] => (Int, *)Right[Int]=> (*, Int)直感的にIntを 左側の世界のInt,右側の世界のInt というふうに分離することはできますよね?A と B を完全に分離するような属性をそれぞれにつけた集合 A*, B* を考え,それらの要素すべてを集めたものが,A と B の直和集合だといえます.その定義のひとつとして (a:A, *) (*, b:B) といった形があるというふうに考えるとわかりやすい気がします

#2-1 前回躓いた直和集合について Scalaでの例:Eitherは直和型 Left, Right がそれぞれ Either を継承することにより直和型を実現しているfinal case class Left[+A, +B](a: A) extends Either[A, B] { def isLeft = true def isRight = false}この方法ではすでに定義されている Int と String の直和型 IntOrString を作ることが出来ない.このような場合は,型クラスというものを用いる詳細は http://nekogata.hatenablog.com/entry/2014/09/07/174814

#2-1 前回躓いた直和集合について Scalaでの例: Optionは直和型 val str1: Option<String> = Some(“hoge”)val str2: Option<String> = NoneOption[T] には Some[T] か None が入るつまり 集合 Option[T] の要素は,集合 Some[T] の全ての要素と 集合 None の全ての要素※Java8のOptionalは実装を見た感じ直和型ではないOptional<String> hoge;hoge = Optinal.of(“hoge”); // <= こいつはOptinalのインスタンスhoge = Optinal.empty(); // <= こいつもOptinalのインスタンス

#2-1 前回躓いた直和集合について Haskellでの例: Maybeは直和型 let hoge = Just 10 :: Maybe Intlet hoge = Nothing :: Maybe Intlet hoge = Just “hoge” :: Maybe Int -- => Error!let hoge = Just 1.0 :: Maybe Int -- => Error!

#2-1 前回躓いた直和集合について Swiftでの例: Optionalは直和型 Optional<T>型と ImplicitlyUnwrappedOptional<T>型 があるらしいenum Optional<T>: … { case None; case Some(T) }let foo: Int? = 1 //=> Now foo = { Some(1) }foo + 1 //=> Errorif let foo = foo { foo + 1 // => 2}

#2-2 ラムダ計算のさわり

#2-2 ラムダ計算のさわり いきなり形式的な議論に入る前に ラムダ計算のさわりの雰囲気を 理解をしておきたい

#2-2 ラムダ計算のさわり そもそもの計算について考えてみる 計算とは何なのか?

#2-2 ラムダ計算のさわり そもそもの計算について考えてみる 計算とは何なのか?   いろいろごちゃごちゃやってるけど   結局は入力から出力を得る関数だよね

#2-2 ラムダ計算のさわり じゃあ関数とは何なのか

#2-2 ラムダ計算のさわり じゃあ関数とは何なのか あっ、これ、進研ゼミでならったやつだ!   f(x) = x + 1

#2-2 ラムダ計算のさわり じゃあ関数とは何なのか あっ、これ、進研ゼミでならったやつだ!   f(x) = x + 1 ん?ここにでてくる f ってなんやねん! おれはただ、 入力に1を足した出力を得たいだけなんや!!

#2-2 ラムダ計算のさわり じゃあ関数とは何なのか あっ、これ、進研ゼミでならったやつだ!   f(x) = x + 1 ん?ここにでてくる f ってなんやねん! おれはただ、 入力に1を足した出力を得たいだけなんや!! →関数名は計算操作を考えたとき本質的でない

#2-2 ラムダ計算のさわり ラムダ抽象の導入 f(x) = x + 1 の本質は、 x という入力を受け取り x + 1 という出力を返すということ この本質だけを抽出して λx. x + 1 と書くことにする(テストに出る) こいつをλ抽象(λ-abstraction)よぶ

#2-2 ラムダ計算のさわり ラムダ抽象に慣れ親しむ ~関数適用~ f(x) = x + 1 として f(2) を書きたいときどうすりゃいいねん → λx. x + 1 (2) と書く これを「λx. x + 1 を 2 に適用する」という

#2-2 ラムダ計算のさわり ラムダ抽象に慣れ親しむ ~関数適用~ → λx. x + 1 (2) こうしたときに 仮引数 x を 2 で置き換えて 計算を進めることができる → λx. x + 1 (2) = 2 + 1 = 3 これをβ簡約(β-reduction)とよぶ

#2-2 ラムダ計算のさわり ラムダ抽象に慣れ親しむ ~カリー化~ そういえば進研ゼミで  f(x, y) = x + y なんて関数も習ったぞ! たしか... 多変数関数とか呼ばれてた気がする... これどうすんねん

#2-2 ラムダ計算のさわり ラムダ抽象に慣れ親しむ ~カリー化~ f(x, y) = x + y は  λx. λy. x + y と本質的に計算は同じ!

#2-2 ラムダ計算のさわり ラムダ抽象に慣れ親しむ ~カリー化~ f(x, y) = x + y は  λx. (λy. x + y) と本質的に計算は同じ! ところで、これは x を引数にとり、 「1変数関数を返す」関数にも見える!すごい! これをカリー化(Currying)とよぶ

#2-2 ラムダ計算のさわり ラムダ抽象に慣れ親しむ ~カリー化~ いくつか言葉の導入  λx. x + y について    x は束縛されているという  y は自由であるという

#2-2 ラムダ計算のさわり ラムダ抽象に慣れ親しむ ~練習問題~ (1)  f(x) = 2x + 3 をλ抽象で書け

(2)  f(x, y) = 3x + y をλ抽象で書け

(3)  f(x, y) = 3x + y + xy をλ抽象で書け

#2-2 ラムダ計算のさわり ラムダ抽象に慣れ親しむ ~練習問題~ (1)  f(x) = 2x + 3 をλ抽象で書け λx. 2x + 3

(2) f(x, y) = 3x + y をλ抽象で書け λx. λy. 3x + y (3) f(x, y) = 3x + y + xy をλ抽象で書け λx. λy. 3x + y + xy

#2-3 型なしラムダ計算 ここからお堅いはなし

参考書:プログラム意味論(共立出版)

#2-3 型なしラムダ計算 λ式の定義 ラムダ計算が扱う式をλ式と呼び,次のようにに定義する e(λ式) ::= x (変数) | λx.e (λ抽象) | e1 e2 (関数適用)

λ式は変数・λ抽象・関数適用から構成される

#2-3 型なしラムダ計算 「ラムダ計算の実行」を定義する #2-2 でやったようにラムダ計算は実行できる この実行の手順も形式的に定義する必要がありますよね?

#2-3 型なしラムダ計算 「ラムダ計算の実行」を定義する #2-2 でやったようにラムダ計算は実行できる この実行の手順も形式的に定義する必要がありますよね? → 計算の実行を表す β-簡約(β-reduction) を導入

#2-3 型なしラムダ計算 「ラムダ計算の実行」を定義する β-簡約(β-reduction) (λx.M) N →β M[x := N]

関数適用そのものを表している (左式) は, Mの中にある束縛されている x を N に置き換えた式 にできる

#2-3 型なしラムダ計算 「ラムダ計算の実行」を定義する α-変換(α-conversion) λx.M →α λy. M[x := y]

変数の名前は本質的でなく 置き換えができることを表す変換 f(x) = x + 1 と f(y) = y + 1 は 計算の意味的にはおなじですよね?

#2-3 型なしラムダ計算 「ラムダ計算の実行」を定義する η-変換(η-conversion) λx.M x →η M

関数の外延性を表す変換 平たく言えば (左辺) に対してどんな値を与えても常に同じλ式 を返すとき,(左辺) 自体をそのラムダ式に置き換えることが出来るってこと

#2-3 型なしラムダ計算 ラムダ計算はチューリング完全 チューリングマシンについて詳しくないので 説明できる人よろしく なんかおおよそプログラミングによって実現できるようなことを、ラムダ計算でも実現できるよということらしい

#2-3 型なしラムダ計算 ラムダ計算はチューリング完全 チューリングマシンについて詳しくないので 説明できる人よろしく なんかおおよそプログラミングによって実現できるようなことを、ラムダ計算でも実現できるよということらしい → マジで!?チョーウケルwwww   (おいおい、冗談もほどほどにしろよ...)

#2-3 型なしラムダ計算 ラムダ計算による if ... else ... 例えば if ... else ... の機構 true は λt,λf.t false は λt.λf.f とすると if e1 then e2 else e3 を e1e2e3 として うまい具合に if ... else ... の構造を表せる

#2-3 型なしラムダ計算 ラムダ計算による if ... else ... 例えば if ... else ... の機構 true は λt,λf.t false は λt.λf.f とすると if e1 then e2 else e3 を e1e2e3 として うまい具合に if ... else ... の構造を表せる 確認してみよう ←やってみて!!すごい!!

#2-3 型なしラムダ計算 ラムダ計算による自然数とその演算の表現 以下のように自然数をλ式で表せる. これをチャーチ数とよぶ 0 = λs.λz.z 1 = λs.λz.sz 2 = λs.λz.s(sz)

ファッ?!

#2-3 型なしラムダ計算 ラムダ計算による自然数とその演算の表現 0 = λs.λz.z 1 = λs.λz.sz 2 = λs.λz.s(sz) 足し算を plus =λg1.λg2.λs.λz.g2sg1z と定義てやればこの定義に納得できる 計算してみよう!マジすごい!

#2-3 型なしラムダ計算 ラムダ計算による自然数とその演算の表現 0 = λs.λz.z 1 = λs.λz.sz 2 = λs.λz.s(sz) 足し算を plus =λg1.λg2.λs.λz.g2sg1z と定義てやればこの定義に納得できる 計算してみよう!マジすごい! ところで自然数は 0, 1, 2 じゃだめなのか?

#2-3 型なしラムダ計算 λ式の定義(復習) ラムダ計算が扱う式をλ式と呼び,次のようにに定義する e(λ式) ::= x (変数)  | λx.e (λ抽象) | e1 e2 (関数適用)

λ式は変数・λ抽象・関数適用から構成される

実はλ式ではそもそも数字すら  定義されていないので  自分で定義していく必要がある 

#2-3 型なしラムダ計算 λ式の定義(復習) ラムダ計算が扱う式をλ式と呼び,次のようにに定義する e(λ式) ::= x (変数)  | λx.e (λ抽象) | e1 e2 (関数適用)

λ式は変数・λ抽象・関数適用から構成される 実は数字そのものから 構成していく必要があった!!!!

実はλ式ではそもそも数字すら  定義されていないので  自分で定義していく必要がある