(高階関数を理解していることを確認してください) . Alonzo Churchの型なしラムダ計算では、関数が唯一のプリミティブ データ型です。数値、ブール値、リストなどはなく、関数のみです。関数は引数を 1 つしか持つことができませんが、関数は関数を受け入れたり返したりすることができます。これらの関数の値ではなく、関数自体です。したがって、数値、ブール値、リスト、およびその他のタイプのデータを表すには、無名関数がそれらを表す賢い方法を考え出す必要があります。チャーチナンバーは、自然数を表す方法。型指定されていないラムダ計算の 3 つの最も基本的な構造は次のとおりです。
λx.x
恒等関数は、何らかの関数を受け取り、すぐにそれを返します。
λx.x x
、自己申請。
λf.λx.f x
、関数適用、関数と引数を取り、関数を引数に適用します。
0、1、2 を関数以外の何物でもないものとしてどのようにエンコードしますか? どういうわけか、量の概念をシステムに組み込む必要があります。関数のみがあり、すべての関数は 1 つの引数にのみ適用できます。量に似たものはどこに見られますか? 関数をパラメーターに複数回適用できます。関数の 3 回の呼び出しの繰り返しには明らかに量感がありますf (f (f x))
。それでは、ラムダ計算でエンコードしましょう。
- 0 =
λf.λx.x
- 1 =
λf.λx.f x
- 2 =
λf.λx.f (f x)
- 3 =
λf.λx.f (f (f x))
等々。しかし、0 から 1、または 1 から 2 になるにはどうすればよいでしょうか。数値を指定すると、1 ずつインクリメントされた数値を返す関数をどのように記述しますか? 用語が常に f で始まり、 fλf.λx.
の有限の繰り返し適用があるという教会数字のパターンが見られるため、何らかの方法で の本体に入り、別の にラップする必要があります。縮小せずに抽象化の本体をどのように変更しますか? 関数を適用し、本体を関数でラップしてから、新しい本体を古いラムダ抽象化にラップできます。しかし、引数を変更したくないので、同じ名前の値に抽象化を適用します: 、ただし、これは必要なものではありません。λf.λx.
f
((λf.λx.f x) f) x → f x
((λf.λx.f x) a) b) → a b
そのため、add1
にλn.λf.λx.f ((n f) x)
適用n
しf
てx
から式をボディに還元し、f
そのボディに適用してから、 で再び抽象化しλf.λx.
ます。演習:それが本当であることを確認するには、すぐにβ 還元(λn.λf.λx.f ((n f) x)) (λf.λx.f (f x))
を学び、 2 を 1 ずつ増やすように還元します。
本体を別の関数呼び出しにラップすることの背後にある直感を理解したところで、2 つの数値の加算をどのように実装するのでしょうか? λf.λx.f (f x)
(2) とλf.λx.f (f (f x))
(3) を指定すると (5) を返す関数が必要λf.λx.f (f (f (f (f x))))
です。2 を見てください。それを 3 の本体に置き換えることができたらどうでしょうか。3 のボディを取得するには、それを適用してから. ここで 2 をに適用しますが、それを ではなく 3 の本体に適用します。次に、もう一度ラップします。x
f (f (f x))
f
x
f
x
λf.λx.
λa.λb.λf.λx.a f (b f x)
結論:a
2 つの数字を加算するb
には、両方とも教会の数字として表されます。 inを の本体に置き換え て、+ = . これを実現するには、 に適用し、次に に適用します。x
a
b
f (f x)
f (f (f x))
f (f (f (f (f x))))
a
f
b f x