純粋関数型言語Ruby
Rubyで型なしラムダ計算
郡司啓(@gunjisatoshi)
宣伝
今日のねらい
(型なし)ラムダ計算とは
「型がない」と言うより「型が一つ(関数)しかない」
関数とは
Rubyで(型なし)ラムダ計算
Proc.call(x)のxはProcオブジェクトが前提、戻り値は必ずProcオブジェクト
カリー化と自由変数
と、
は同じ
Proc#callとProcのリテラル表現
Churchブール値
二つの引数を取り、必ず一つ目の引数の値をそのまま返す関数をTrueと定義する
my_t = ->(t) {->(f) { t } }
二つの引数を取り、必ず二つ目の引数の値をそのまま返す関数をFalseと定義する
my_f = ->(t) {->(f) { f } }
def my_to_bool(b)
b[true][false]
end
Churchブール値で論理演算
my_and = ->(b) { ->(c) { b[c][my_f] } }
my_or = ->(b) { ->(c) { b[my_t][c] } }
my_not = ->(b) { b[my_f][my_t] }
デモ1
データ構造の表現
my_pair = ->(f) { ->(s) { ->(b) { b[f][s] } } }
my_first = ->(p) { p[my_t] }
my_second = ->(p) { p[my_f] }
Church数
二つの引数を取り、1つ目の引数の関数を2つ目の引数の値にn回適用した関数で自然数を表現する
my_zero = ->(s) {->(z) { z } } (実はFalseと同じ)
my_one = ->(s) {->(z) { s[z] } }
my_two = ->(s) {->(z) { s[s[z]] } }
my_three = ->(s) {->(z) { s[s[s[z]]] } }
一つの引数(二つの引数を取り、1つ目の引数の関数を2つ目の引数の値にn回適用した関数)を取り、「二つの引数を取り、1つ目の引数の関数を2つ目の引数の値にn+1回適用する関数」を返す
my_succ = ->(n) { ->(s) { ->(z) { s[n[s][z]] } } }
def my_to_i(n)
n[:succ.to_proc][0]
end
Church数同士で計算
my_plus = ->(m) { ->(n) { ->(s) { ->(z) { m[s][n[s][z]] } } } }
my_times = ->(m) { ->(n) { ->(s) { m[n[s]] } } }
「前の数」と「引き算」
デモ2
制御構造の表現
my_if = -> (l) { -> (m) { -> (n) { l[m][n] } } }
y_combinator = -> (f) {
(-> (q) { -> (m) { f[q[q]][m] } })[
-> (q) { -> (m) { f[q[q]][m] } } ] }
「等しさ」の判定
応用:フィボナッチ数を求めてみる
my_if[my_iszero[n]][
-> (x) { my_zero }][
my_if[my_equal[n][my_one]][
-> (x) { my_one }][
-> (x) { my_plus[f[my_pred[n]]][
f[my_pred[my_pred[n]]]]}]][my_zero] } }
デモ3
まとめ