私を SKI に連れてって
Transcript of 私を SKI に連れてって
私を SKI に連れてって201x年m月d日 第3回 OUCC LT会 のつもりだったけど長すぎてボツ
お前誰って人のための
すしす
Twitter: @susisu2413
GitHub: susisu
本日の予定
λ計算入門
SKIコンビネータ入門
Lazy K の紹介
λ計算入門
λ計算とは
"計算"を関数とその適用でモデル化したもの
関数型言語の理論的基盤
ここでは型無しλ計算を紹介します
型付きもあるんだよ
λ式
変数 a, b, c, ..., x, y, z, ...
λ抽象 λx.x, λx.λy.(x y), ...
関数適用 (f x)
λ式λx.λy.* は λx y.* と略記する
関数適用の括弧は曖昧でない場合は省略する
x y = (x y)
関数適用は左結合
f x y = ((f x) y)
α-変換
λ抽象の束縛変数名は重要ではない
例えば λx.x = λy.y
束縛変数名を別の名前に置き換える操作をα-変換と呼ぶ
α-変換で同じλ式にできるなら等価
β-簡約
要はただの関数適用の評価
λ抽象内部の束縛変数を全て引数で置き換える
例えば (λx.f x) y = f y
β-簡約で同じλ式にできるなら等価
η-変換どんな引数に対しても同じ結果になるならば、それらは同じといってもいいよね (外延的等価性)
例えば f = λx.f x
これらの間の変換をη-変換と呼ぶ
η-変換で同じλ式にできるなら等価
Quiz
1. λx.x y = λy.y y ?
2. (λx.λx.x y) y = λy.y y ?
3. λx.x = (λx y z.x z (y z)) (λx y.x) (λx y.x) ?
Answer
1. No
2. No
3. Yes
λ計算
λ式と変換・簡約の規則をまとめてλ計算と呼ぶ
λ計算はチューリング完全
どんな手続き (プログラム) もλ式で表現できる
Q. どう計算するの
入力 x が与えられるとする
手続きを表すλ式 p をうまく選べば、例えば y = p x で出力が得られる
y を変換・簡約すれば解読できそう
Q. 数値とかどうするの
数値がなければ、λ式を使えばいいじゃない
真理値がなければ、λ式を使えば
リストがなければ、λ式を
Church数
λ式で自然数を表わすひとつの方法
Church数
0 := λf x.x
1 := λf x.f x
2 := λf x.f (f x)
...
Church数次の自然数を求める関数 Succ
Succ := λn f x.f (n f x)
足し算 Add
Add := λa b f x.a f (b f x)
掛け算はどうなるか考えてみよう
Church数
掛け算 Mul
Mul := λa b f x.a (b f) x
Church数前の自然数を求める関数 Pred
Pred := λn f x. n (λg h.h (g f)) (λy.x) (λy.y)
ただし Pred 0 = 0
キリがないのでこれ以上はやめる
Church真理値
True := λx y.x
False := λx y.y
If := λp x y.p x y = λp.p
Church真理値
AND, OR, NOT
And := λp q.p q False
Or := λp q.p True q
Not := λp.p False True
述語の例
Church数 n が 0 かどうか調べる
0 = λf x.x だった
IsZero := λn. n (λx.False) True
だんだんとプログラムが書ける気がしてきませんか?
Church対
リストや木構造は対 (pair) の連鎖で表現できる
[1, 2, 3] = (1, (2, (3, 空)))
対をλ式で表現できればいいよね
Church対
対をつくる関数 Cons
Cons := λx y c.c x y
かんたん!べんり!
Church対
対の前後を取り出す関数 Car, Cdr
Car := λp.p (λx y.x)
Cdr := λp.p (λx y.y)
Church対
空 (リストの終端) は?
好きなものを使えば良い
False が一般的っぽい?
Quiz入力 x が 2 つのChurch数 a, b のChurch対で与えられる
出力 y = p x が、a = 0 なら y = b、それ以外なら y = a * b となる p を書いてみよう
Succ などこれまでに定義したものは使ってもよい
Answer
例: λx.IsZero (Car x) (Cdr x) (Mul (Car x) (Cdr x))
繰り返し処理
繰り返し処理を関数で表現したい時は、再帰的な関数を書けばいい
f(0) = 1, f(n) = n * f(n - 1)
けれどλ計算では"再帰的な定義"ができないので、単純には書けない
関数の不動点
関数 f に対して、f x = x となる ような x を f の不動点と呼ぶ
Fix関数 f を引数にとり、その不動点を返す関数 Fix が存在したとする
Fix f = f (Fix f)
f = λx n. IsZero n True (x (Pred n))
Fix f n は n 回の空ループになる
Fix
Fix があれば、繰り返し処理を表現することが出来る
このような関数 Fix をなんとかλ式だけで表現できないか?
Fix
できる!
Fix := λf.(λx.f (x x)) (λx.f (x x))
実際に Fix f = f (Fix f) となるか確かめてみるとよいです
λ計算のまとめλ計算は、計算を関数とその適用でモデル化したもの
チューリング完全
数値も真理値もリストもλ式で表現できる
繰り返し処理だってできちゃう
SKIコンビネータ入門
コンビネータ計算とは
!
高階関数 (コンビネータ) を用いて計算を行う
λ計算と同じく、簡約が存在
(P x y z ...) = E
簡約の結果 E は、コンビネータ P の定義による
コンビネータの例
B x y z = x (y z)
C x y z = x z y
W x y = x y y
コンビネータの例
S x y z = x z (y z)
K x y = x
I x = x
実は I = S K K とも書ける
SKIコンビネータ計算
S、K、I の 3 つのコンビネータを、"基底"として用いる
他のコンビネータは S、K、I の組み合わせで表現する
スキーとは関係がない
SKI で何ができるかSKIコンビネータ計算はチューリング完全
λ抽象と相互に変換することができる
SKI→λ は自明
λ→SKI については Wikipedia の「コンビネータ論理」のページを見るとよいかも
SKI でChurch数0 := KI
1 := I
2 := S(S(KS)K)I
...
Succ := S(S(KS)K)
SKI でChurch真理値
True := K
False := KI
If := I
SKI でChurch対
Cons := S(S(KS)(S(KK)(S(KS) (S(K(SI))K))))(KK)
笑っちゃうよね
SKI で Fix (Y)
Fix よりも、不動点コンビネータ Y と呼ぶことが多い (きがする)
Y := SSK(S(K(SS(S(SSK))))K)
Quiz
Church数の掛け算、足し算を SKI で書いてみよう
足し算の方が難しくなるはず
Answer
Mul = S(KS)K
Add = S(KS)(S(K(S(KS))) (S(KK)))
SKI のまとめS, K, I という 3 つのコンビネータと適用だけで計算をする
チューリング完全
λ抽象と相互に変換できる
データも表せるし、繰り返し処理もできる
だんだん自分の SKI 力がどれほどのものか 確かめたくなってきましたよね?
Lazy K
こころ夏目漱石
“精神的に向上心のないものは馬鹿だ”
“精神的に向上心のないものは馬鹿だ”Eager K
Lazy K とは
SKIコンビネータ計算をベースにした言語
遅延評価、故に Lazy
対義語: Eager, Strict
こころの K とは関係がない
遅延評価
結果に必要のない部分を評価 (計算) しないための仕組み
例えば K x y = x の y は必要ないので、評価を省略したい
遅延評価
ほとんどのプログラミング言語では、引数は適用の前に評価される
Lazy K では、引数はそれ自身が関数として適用に使われるまで評価が遅延される (使わなければ評価されない)
これによって、Yコンビネータによる 停止するループや、無限の長さをもつリストが実現できる
入力
入力は文字コードをChurch数で表現したものの、Church対によるリストで与えられる
入力の終端はChurch数 256
256 = SII(SII(S(S(KS)K)I))
ただし"リストの終端"ではない
出力
入力 X とプログラム P に対して、出力は Y = P X
出力も文字コードをChurch数で表現したものの、Church対によるリスト
出力の終端も 256 以上のChurch数
記法
Lazy K には 4 つの記法がある
コンビネータ計算 …… S(KI)I
unlambda …… ``s`kii
Iota …… * と i だけ
Jot …… 0 と 1 だけ
記法 (その他)
空白、改行は無視される
# の後は改行までコメント
空ファイルは I
Lazy K 処理系http://esoteric.sange.fi/essie2/download/lazy-k.zip
Windows 用のバイナリ同梱
GCC でコンパイルするときは lazy.cpp にパッチを当てる
https://gist.github.com/susisu/831e06af2ddee14938c8
Lazy K 処理系
実行はファイルから、または -e で直接プログラムを指定
$ lazy <file>
$ lazy -e <program>
Lazy K 処理系ためしになにか動かしてみる
$ lazy eg/calc.lazy
$ lazy -e SKK
中身を見るともっとたのしい
SKI を組み合わせて、君だけの最強プログラムを作ろう!
Lazy K の闇は深い
参考文献
大体 Wikipedia を読めばいいと思う
ラムダ計算
コンビネータ論理
SKIコンビネータ計算
不動点コンビネータ
参考文献
The Lazy K Programming Language
https://tromp.github.io/cl/lazy-k.html
↑の日本語訳
http://legacy.e.tir.jp/wiliki?%CB%DD%CC%F5%3A%A5%D7%A5%ED%A5%B0%A5%E9%A5%DF%A5%F3%A5%B0%B8%C0%B8%ECLazy_K