
数理論理学において、ラムダ計算( λ計算とも表記される)は、変数束縛と置換を用いて関数の抽象化と適用に基づく計算を表現する形式体系である。本稿の主題である型なしラムダ計算は、汎用機械、すなわち任意のチューリングマシンをシミュレートできる(そしてその逆も可能な)計算モデルである。これは、数学者アロンゾ・チャーチが1930年代に数学の基礎に関する研究の一環として導入したものである。チャーチは1936年に論理的に一貫性のある定式化を発見し、1940年にそれを文書化した。
ラムダ計算は、形式構文によって定義されるラムダ項の言語と、それらの項を操作するための変換規則のセットから構成されます。BNFでは、構文は次のようになります。変数無限個の名前の範囲。用語すべてのラムダ項にわたる範囲。これは次の帰納的定義に対応します。
ラムダ項は、以下の3つの規則を繰り返し適用することで得られる場合に構文的に有効です。便宜上、ラムダ項を記述する際に括弧を省略できる場合が多くあります。詳細は「ラムダ計算の定義 § 表記法」を参照してください。
ラムダの用語では、何らかの囲み関数のパラメータではない変数の出現は、は無料であると言われています。学期中束縛されている. 内の他の変数の任意の自由な出現自由のまま。
例えば、この用語では、 両方そして無料で発生します。、無料ですが、本体(つまりドットの後)は自由ではなく、(パラメータに)束縛されていると言われます。無料です、それは2 回出現しますで―一方は束縛され、もう一方は自由である。
は自由変数の集合ですつまり、自由変数として出現する変数少なくとも一度は。帰納的に以下のように定義できる。
表記法キャプチャ回避置換を表す:置換自由出現ごとにで変数キャプチャを回避しながら。[ a ]この操作は、以下のように帰納的に定義されます。
ラムダ項を同等のラムダ項に還元することを可能にする「等価性」と「還元」の概念がいくつか存在する。[ 3 ]
還元式(redex)とは、還元規則のいずれかによって還元できる部分項を指します。例えば、は置換を表現するβ-レデックスであるのためにで. redex が還元される式をその還元式と呼び、は。
ラムダ計算はチューリング完全であり、つまり、あらゆるチューリングマシンをシミュレートするために使用できる普遍的な計算モデルです。[ 4 ]その名前の由来であるギリシャ文字のラムダ(λ)は、関数内の変数を束縛することを示すためにラムダ式とラムダ項で使用されます。
ラムダ計算は、型なしまたは型ありのいずれかです。型付きラムダ計算では、関数は、与えられた入力の「型」のデータを受け入れることができる場合にのみ適用できます。型付きラムダ計算は、型なしラムダ計算よりも厳密に弱いです。型なしラムダ計算は、型なしラムダ計算よりも表現できるものが少ないという意味で、この記事の主な主題です。一方、型付きラムダ計算では、より多くのことを証明できます。たとえば、単純型付きラムダ計算では、すべての評価戦略はすべての単純型付きラムダ項に対して終了するという定理があります[ 5 ] 。一方、型なしラムダ項の評価は終了する必要はありません(下記参照)。型付きラムダ計算が数多く存在する理由の一つは、型なしラムダ計算でできることの多くを実現しつつ、ラムダ計算に関する強力な定理を証明できる能力を放棄したくないという願望があるからだ。
ラムダ計算は、数学、哲学[ 6 ] 、言語学[ 7 ] [ 8 ]、コンピュータ科学[ 9 ] [ 10 ]など、さまざまな分野で応用されています。ラムダ計算は、プログラミング言語の理論の発展において重要な役割を果たしてきました。関数型プログラミング言語はラムダ計算を実装しています。ラムダ計算は、圏論における現在の研究テーマでもあります[ 11 ]。
ラムダ計算は、数学の基礎に関する研究の一環として、1930年代に数学者アロンゾ・チャーチによって導入されました。[ 12 ] [ c ]元のシステムは、1935年にスティーブン・クリーネとJB・ロッサーがクリーネ・ロッサーのパラドックスを提唱した際に、論理的に矛盾していることが示されました。[ 13 ] [ 14 ]
その後、1936年にチャーチは計算に関連する部分だけを分離して発表し、現在では型なしラムダ計算と呼ばれているものを発表した。[ 15 ] 1940年には、計算能力は劣るものの論理的に一貫性のあるシステムも導入し、これは単純型付きラムダ計算として知られている。[ 16 ]
プログラミング言語との関係が明確になった1960年代までは、ラムダ計算は単なる形式体系に過ぎませんでした。リチャード・モンタギューをはじめとする言語学者たちが自然言語の意味論に応用したおかげで、ラムダ計算は言語学[ 17 ]とコンピュータ科学[ 18 ]の両方で確固たる地位を築き始めました。
チャーチがギリシャ文字ラムダ(ラムダ計算における関数抽象化の表記法として ) が用いられるようになったのは、おそらくチャーチ自身による説明が矛盾していたことが一因であろう。CardoneとHindley(2006)によれば、次のようになる。
ところで、チャーチはなぜ「「?[1964年のハラルド・ディクソン宛の未発表の手紙]の中で、彼はそれが「「ホワイトヘッドとラッセルによってクラス抽象化に使用され、最初に修正された」" に "「関数抽象とクラス抽象を区別し、そして変更する」" に "印刷のしやすさを考慮して。
この起源は[Rosser, 1984, p.338]でも報告されている。一方、晩年、チャーチは2人の質問者に、その選択はもっと偶然だったと語った。象徴が必要で、たまたま選ばれただけだ。
ダナ・スコットも様々な公開講演でこの問題を取り上げています。[ 19 ] スコットは、かつてラムダ記号の起源についてチャーチの元教え子で義理の息子であるジョン・W・アディソン・ジュニアに質問したところ、アディソンは義父に絵葉書を送ったと述べています。
チャーチ教授へ
ラッセルはイオタ演算子を、ヒルベルトはイプシロン演算子を持っていました。なぜあなたはラムダを演算子として選んだのですか?
スコットによれば、チャーチの返答は、ハガキに「イーニー、ミーニー、マイニー、モー」という注釈を添えて返送することだけだった。
計算可能な関数は、コンピュータ科学と数学における基本的な概念です。ラムダ計算は、計算の性質を形式的に研究するのに役立つ、計算のためのシンプルな意味論を提供します。ラムダ計算は、その意味論をシンプルにする2つの簡略化を取り入れています。 最初の簡略化は、ラムダ計算では関数を「匿名」に扱い、明示的な名前を付けないということです。たとえば、関数
匿名形式で書き直すと次のようになります
(これは「タプル」と読みます)そしてマッピング先「) [ d ]同様に、関数
匿名形式で書き直すと次のようになります。
入力は単純にそれ自身にマッピングされます。[ d ]
2つ目の簡略化は、ラムダ計算では単一の入力の関数のみを使用するという点です。例えば、2つの入力を必要とする通常の関数は、関数は、単一の入力を受け取り、出力として別の関数を返す同等の関数に書き換えることができ、その関数もまた単一の入力を受け取ります。たとえば、
再加工して
この手法はカリー化と呼ばれ、複数の引数を取る関数を、それぞれが単一の引数を取る関数の連鎖に変換する。
機能の適用関数を引数 (5, 2) に渡すと、すぐに
一方、カレー風味バージョンの評価にはもう1つのステップが必要となる。
同じ結果にたどり着く。
ラムダ計算では、関数は「第一級の値」とみなされるため、関数は入力として使用することも、他の関数からの出力として返すこともできます。例えば、ラムダ項恒等関数を表す。。 さらに遠く、定数関数を表す常に返す関数入力に関係なく。関数に対して作用する関数の例として、関数合成は次のように定義できます。。
β還元はα変換まで合流的であることが示せる(つまり、2つの正規形が、一方を他方にα変換できる場合に等しいとみなされる)。チャーチ・ロッサーの定理によれば、与えられたラムダ項から始まり、最終的に終了する還元ステップの任意の特定のシーケンスは、同じβ正規形を生成する。しかし、書き換え規則としてのβ還元による型なしラムダ計算は、強く正規化も弱く正規化もせず、 Ωのような正規形を持たない項が存在する。
個々の項について考えると、強正規化項と弱正規化項はいずれも一意の正規形を持つ。強正規化項については、どのような縮約戦略を用いても必ず正規形が得られるが、弱正規化項については、一部の縮約戦略では正規形を見つけられない場合がある。
基本的なラムダ計算は、次のサブセクションi、ii、iii、および§ ivで示されているように、算術、ブール値、データ構造、および再帰をモデル化するために使用できます。
ラムダ計算における自然数の定義方法はいくつかありますが、最も一般的なのはチャーチ数であり、これは次のように定義できます。
などなど。あるいは、関数に複数の非カリー化引数を許可する別の構文を使用することもできます。
チャーチ数とは、高階関数です。これは、引数が1つの関数fを受け取り、別の引数が1つの関数を返します。チャーチ数nは、関数f を引数として受け取り、 fのn乗合成、つまり関数f をn回自身と合成したものを返す関数です。これはf ( n )と表記され、実際にはfのn乗(演算子として考えた場合) です。f ( 0)は恒等関数として定義されます。関数の合成は結合法則を満たすため、単一の関数fのこのような繰り返し合成は、指数法則f ( m ) ∘ f ( n ) = f ( m+n )および( f ( n ) ) ( m ) = f ( m*n )の2 つの法則に従います。これが、これらの数が算術に使用できる理由です。 (チャーチのオリジナルのラムダ計算では、ラムダ式の仮引数は関数本体に少なくとも一度は出現する必要があり、そのため上記の0の定義は不可能だった。)
プログラムの解析によく用いられるチャーチ数nを考える一つの方法は、「 n回繰り返す」という命令として捉えることです。例えば、以下で定義するPAIR関数とNIL関数を用いると、空のリストから始めて、「別のx要素を先頭に追加する」という命令をn回繰り返すことで、すべてxに等しいn個の要素からなる(連結)リストを構築する関数を定義できます。ラムダ項
チャーチ数nとxが与えられたとき、 n個のアプリケーションのシーケンスを作成する
繰り返す内容や、その繰り返される関数が適用される引数を変化させることで、非常に多様な効果を実現できる。
チャーチ数nを受け取り、与えられた関数fをさらに1回適用することで、そのn+1の後継数を返す後継関数を定義できます。ここで、( nfx )は「xから始まるfのn回の適用」を意味します。
fのm番目の合成とfのn番目の合成を組み合わせると、 fのm + n番目の合成が得られるので、f ( m ) ∘ f ( n ) = f ( m+n )となり、加算は次のように定義できます。
PLUSは、2つの自然数を引数として受け取り、自然数を返す関数と考えることができます。
そして
これらはベータ同値なラムダ式です。数値にmを加えることは、後続演算をm回繰り返すことで実現できるため、別の定義は次のようになります。
同様に、( f ( n ) ) ( m ) = f ( m*n )に従って、乗算は次のように定義できます。
したがって、チャーチ数の乗算は、関数としてのそれらの合成に他ならない。
mとnを掛け合わせることは、nを0から始めてm回繰り返し足すことと同じだからである。
べき乗とは、ある数をそれ自身と繰り返し掛け合わせることであり、関数としては、教会数とそれ自身を繰り返し合成することに相当する。そして、教会数とはまさに繰り返し合成のことなのである。
あるいは、ここでも、
簡単に言うと、
しかしそれは、すでに上で述べたPOWのイータ展開版にすぎません。
2 つの式PRED (SUCC n ) = nおよびPRED 0 = 0で指定される前任者関数は、かなり複雑です。
can be validated by showing inductively that if T denotes (λg.λh.h (gf)), then T(n)(λu.x) = (λh.h(f(n−1)(x))) for n > 0. Two other definitions of PRED are given below, one using conditionals and the other using pairs. With the predecessor function, subtraction is straightforward. Defining
SUB mn yields m − n when m > n and 0 otherwise.
By convention, the following two definitions (known as Church Booleans) are used for the Boolean values TRUE and FALSE:
Then, with these two lambda terms, we can define some logic operators (these are just possible formulations; other expressions could be equally correct):[22]
We are now able to compute some logic functions, for example:
and we see that AND TRUE FALSE is equivalent to FALSE.
述語とは、ブール値を返す関数です。最も基本的な述語はISZEROで、引数が教会数0の場合はTRUE を返し、それ以外の教会数の場合はFALSE を返します。
次の述語は、最初の引数が2番目の引数以下であるかどうかを判定します。
また、m = n はLEQ m nかつLEQ n mの場合に成り立つので、数値の等価性を表す述語を簡単に構築できます。
述語が利用可能であること、および上記のTRUEとFALSEの定義により、ラムダ計算で「if-then-else」式を簡単に記述できます。たとえば、前任関数は次のように定義できます。
これは、n (λ g .λ k .ISZERO ( g 1) k (PLUS ( g k ) 1)) (λ v .0)がn > 0 のadd n − 1 関数であることを帰納的に示すことで検証できます。
ペア(2要素タプル)は2つの値をカプセル化し、2つの値を渡すハンドラを必要とする抽象化によって表現されます。FIRSTはペアの最初の要素を返し、SECONDは2番目の要素を返します。
連結リストは、空リストを表すNIL、または要素(いわゆるヘッド)とそれよりも小さいリスト(テール)のペアのいずれかになります。述語NULLは、 NILの場合はTRUEを返し、空でないリストの場合はFALSEを返します。
あるいは、NIL := FALSEの場合、構造( l (λ h .λ t .λ z . ... h ... t ...) _on_nil_)により、明示的な NULL テストは不要になります。
ペアの使用例として、( m , n )を( n , n + 1)にマッピングするシフトおよびインクリメント関数は次のように定義できます。
これにより、先行関数の最も分かりやすいバージョンを提供できます。
定義を代入し、結果として得られる式を簡略化すると、簡潔な定義が得られる。
(ここでI := λ x . x)明らかに元の状態に戻ります。
ラムダ計算には、数多くのプログラミング慣用表現が存在します。これらの多くは、もともとラムダ計算をプログラミング言語のセマンティクスの基盤として用いるという文脈で開発されたものであり、実質的にラムダ計算を低レベルプログラミング言語として利用しています。いくつかのプログラミング言語にはラムダ計算(あるいはそれに非常によく似たもの)が断片として含まれているため、これらの手法は実際のプログラミングでも利用されていますが、その場合、難解あるいは馴染みのないものとして認識される可能性があります。
ラムダ計算では、ライブラリは事前に定義された関数の集合という形をとります。これらの関数はラムダ項としては単なる特定の定数です。純粋なラムダ計算には名前付き定数の概念はありません。なぜなら、すべての原子ラムダ項は変数だからです。しかし、定数の名前として変数を分けておき、抽象化を用いてその変数を本体に束縛し、その抽象化を意図した定義に適用することで、名前付き定数を持つことをエミュレートできます。したがって、M (別のラムダ項、「メインプログラム」)でN(明示的なラムダ項)を意味するためにfを使用するには、次のように記述できます。
著者は、上記をより直感的な順序で記述できるようにするために、 let、[ e ]などの構文糖衣を導入することがよくあります。
このような定義を連鎖させることで、ラムダ計算の「プログラム」を、0個以上の関数定義と、それらの関数を用いたプログラムの本体を構成する1つのラムダ項として記述することができる。
このletの注目すべき制約は、名前f をN内で参照できないことです。なぜなら、N は抽象化束縛fのスコープ外であり、そのスコープはMだからです。つまり、letを使用して再帰関数定義を記述することはできません。letrec [ f ]という構成を使用すれば、抽象化束縛fのスコープにNとMの両方が含まれる再帰関数定義を記述できます。あるいは、 Yコンビネータにつながるような自己適用を使用することもできます。
再帰とは、関数が自身を呼び出すことです。このような関数を表す値とはどのようなものでしょうか。定義が自身の中で自身を参照するように、何らかの方法で自身の中で自身を参照する必要があります。この値が値によって自身を含む場合、無限のサイズにならなければならず、それは不可能です。再帰をネイティブにサポートする他の表記法では、定義の中で関数を名前で参照することでこの問題を克服しています。ラムダ計算では、そもそも項に名前がなく、引数の名前、つまり抽象化のパラメータしかないため、これを表現することはできません。したがって、ラムダ式は引数として自身を受け取り、対応するパラメータの名前を介して自身(のコピー)を参照することができます。実際に自身を引数として呼び出した場合は、これはうまく機能します。たとえば、 (λ x . x x ) E = ( EE )は、 Eが再帰呼び出しを表現するために本体内でパラメータを自身に適用する抽象化である場合に再帰を表現します。このパラメータは値としてEを受け取るため、その自己適用は再び同じ(EE)になります。
具体的な例として、階乗関数F( n )を考えてみましょう。これは次のように再帰的に定義されます。
この関数を表すラムダ式では、パラメータ(通常は最初のパラメータ)はラムダ式自体を値として受け取るものと想定されるため、最初の引数としてラムダ式自体を呼び出すと再帰呼び出しになります。したがって、再帰を実現するには、自己参照を意図した引数(ここでは「s」と呼ばれ、「self」または「self-applying」を連想させる)を、関数本体内の再帰呼び出し箇所で常に自身に渡す必要があります。
そして私たちは
ここで、ssはアプリケーション(EE)の結果内で同じ(EE)となり、呼び出しに同じ関数を使用することが再帰の定義です。自己アプリケーションはここで複製を実現し、関数のラムダ式を引数値として次の呼び出しに渡します。これにより、パラメータ名sで参照できるようになり、自己アプリケーションs sを介して必要に応じて何度も呼び出され、その都度ラムダ項F = EEが再作成されます。
アプリケーションは、名前検索と同様に、追加の手順となります。遅延効果も同様です。F全体を最初から内部に保持するのではなく、次の呼び出しまで再作成を遅延させることで、内部に 2 つの有限ラムダ項Eが存在し、必要に応じて後で動的に再作成されることが可能になります。
この自己適用アプローチは問題を解決しますが、各再帰呼び出しを自己適用として書き直す必要があります。書き直しを必要としない汎用的な解決策を求めています。
再帰呼び出しを表す最初の引数を持つラムダ式(ここではG)が与えられた場合、固定小数点コンビネータFIXは、再帰関数(ここではF )を表す自己複製ラムダ式を返します。自己複製は作成時にあらかじめ設定されており、呼び出されるたびに実行されるため、関数を明示的に自身に渡す必要はありません。したがって、呼び出し時に元のラムダ式(FIX G)が自身の中で再作成され、自己参照が実現されます。
実際、このFIX演算子には多くの定義が存在し、その中で最も単純なものは次のとおりです。
ラムダ計算において、Y gはg の不動点であり、次のように展開されます。
さて、引数nに対して階乗関数を再帰的に呼び出すには、単に( Y G) nと呼び出せばよい。例えば、n = 4 の場合、次のようになる。
再帰的に定義された関数はすべて、追加の引数を持つ再帰呼び出しを包含する、適切に定義された高階関数(関数とも呼ばれる)の不動点と見なすことができます。したがって、Yを用いることで、すべての再帰関数をラムダ式として表現できます。特に、再帰を用いることで、自然数の減算、乗算、比較述語を簡潔に定義することが可能になります。
Yコンビネータを厳密なプログラミング言語で直接コーディングすると、そのような言語で使用される評価の適用順序により、内部自己適用を完全に展開しようとする試みが発生します。時期尚早にスタックオーバーフローを引き起こしたり、末尾呼び出し最適化の場合は無限ループを引き起こしたりする。[ 24 ] Y の遅延バリアントであるZ コンビネータは、このような言語で使用できる。内部の自己適用は、イータ展開による追加の抽象化の背後に隠されている。それによって時期尚早な拡大を防ぐ:[ 25 ]
特定の用語には一般的に認められた名称があります: [ 26 ] [ 27 ] [ 28 ]
Iは恒等関数です。SKとBCKW は、任意のラムダ項を表現できる 完全なコンビネータ計算システムを形成します。次のセクションを参照してください。ΩはUUであり、正規形を持たない最小の項です。YIも同様の項です。Y は標準であり、上記で定義されています。また、 Y = BU(CBU)と定義することもできるため、 Y g=g( Y g)となります。上記で定義されたTRUEとFALSE は、一般的にTとFと略記されます。
N が抽象化を持たないラムダ項であり、名前付き定数 (コンビネータ) を含む可能性がある場合、 λ x . Nと同等でありながら抽象化を持たないラムダ項T ( x , N )が存在する(名前付き定数の一部である場合を除く。ただし、これらが非原子的であるとみなされる場合)。これは変数の匿名化とも考えられ、T ( x , N ) はNからxのすべての出現箇所を削除する一方で、 Nにxが含まれている位置に引数の値を代入することは可能である。変換関数T は次のように定義できる。
いずれの場合も、 T ( x , N ) Pの形式の項は、最初のコンビネータI、K、またはS が引数Pを取得することによって、 (λ x . N ) Pの β 還元と同様に還元されます。I はその引数を返します。K N は、 Nにx の自由な出現がない場合の(λ x . N )と同様に、引数を破棄します。S は、引数を適用の 2 つのサブ項に渡してから、最初の結果を 2 番目の結果に適用します。これは、(λ x . MN ) Pが((λ x . M ) P ) ((λ x . N ) P )と同じであるのと同様です。
コンビネータBとCはSと似ていますが、引数をアプリケーションのサブタームの 1 つだけに渡します ( Bは「引数」サブタームに、Cは「関数」サブタームに)。これにより、サブタームの 1 つにxが出現しない場合は、後続のKを節約できます。BとCと比較すると、Sコンビネータは実際には 2 つの機能、つまり引数の並べ替えと、引数を 2 箇所で使用できるように複製する機能を統合しています。Wコンビネータは後者のみを実行するため、SKI コンビネータ計算の代替としてB、C、K、W システムが得られます。
最後のルールの前にさらにルールを追加すると、
表現する-削減、なぜなら(λ x . N x ) P は、このような場合N Pと同じであり、より長いシーケンスS ( K N ) Iの作成を回避できるからである。
型付きラムダ計算は、ラムダ記号 ()匿名関数の抽象化を表す。この文脈では、型は通常、ラムダ項に割り当てられる構文的な性質のオブジェクトである。型の正確な性質は、考慮される計算体系によって異なる(型付きラムダ計算の種類を参照)。ある観点からは、型付きラムダ計算は型なしラムダ計算の改良と見なすことができるが、別の観点からは、型付きラムダ計算はより基本的な理論であり、型なしラムダ計算は1つの型のみを持つ特殊なケースであると考えることもできる。[ 29 ]
型付きラムダ計算はプログラミング言語の基礎であり、MLやHaskellなどの型付き関数型プログラミング言語、そして間接的には型付き命令型プログラミング言語の基盤となっています。型付きラムダ計算はプログラミング言語の型システムの設計において重要な役割を果たします。型付け可能性は通常、プログラムの望ましい特性、例えばメモリアクセス違反を引き起こさないといった特性を捉えます。
型付きラムダ計算は、カリー・ハワード同型性を介して数理論理学や証明論と密接に関連しており、圏のクラスの内部言語とみなすことができる。例えば、単純型付きラムダ計算は、デカルト閉圏(CCC)の言語である。[ 30 ]
項が正規化であるかどうか、また正規化する場合にどれだけの作業が必要かは、使用される縮約戦略に大きく依存します。一般的なラムダ計算縮約戦略には、次のものがあります。[ 31 ] [ 32 ] [ 33 ]
弱い削減戦略はラムダ抽象化の下では削減を行いません。
共有を用いた戦略は、並列処理において「同じ」計算を削減する。
任意の 2 つのラムダ式を入力として受け取り、一方の式が他方の式に還元されるかどうかに応じてTRUEまたはFALSEを出力するアルゴリズムは存在しません。 [ 15 ]より正確には、計算可能な関数ではこの問題を決定できません。これは、歴史的に決定不能性が証明された最初の問題でした。このような証明の場合、通常どおり、計算可能とは、チューリング完全な計算モデルによって計算可能であることを意味します。実際、計算可能性自体はラムダ計算によって定義できます。自然数の関数F : N → Nは、任意のペアx、y ∈ Nに対して、f ( x ) = yとなるようなラムダ式fが存在する場合に限り、計算可能な関数です。ここで、xとyはそれぞれxとyに対応するチャーチ数であり、 = βは β 還元との等価性を意味します。計算可能性とその等価性を定義する他のアプローチについては、チャーチ・チューリングのテーゼを参照してください。
チャーチの計算不能性の証明は、まず与えられたラムダ式が正規形を持つかどうかを判定することに問題を帰着させる。次に、この述語は計算可能であり、したがってラムダ計算で表現できると仮定する。クリーネの以前の研究に基づき、ラムダ式のゲーデル数を構築することで、ゲーデルの第一不完全性定理の証明に非常に近いラムダ式eを構築する。eを自身のゲーデル数に適用すると、矛盾が生じる。
ラムダ計算の計算複雑性の概念は、β 還元のコストが実装方法によって異なる可能性があるため、少し厄介です。[ 34 ] 正確には、式E内の束縛変数Vのすべての出現位置を何らかの方法で見つける必要があり、これは時間コストを意味します。または、自由変数の位置を何らかの方法で追跡する必要があり、これは空間コストを意味します。E内のVの位置を単純に検索すると、 Eの長さnに対してO ( n )になります。ディレクター ストリングは、この時間コストを 2 乗の空間使用と交換する初期のアプローチでした。[ 35 ]より一般的には、これは明示的な置換を使用するシステムの研究につながりました。
2014年に、通常の順序の縮約によって項を縮約するために要するβ縮約ステップの数が妥当な時間コストモデルであることが示されました。つまり、縮約はステップ数に比例する多項式時間でチューリングマシン上でシミュレートできます。[ 36 ]これは、各β縮約でサイズが指数関数的に増加するラムダ項の存在によるサイズ爆発のため、長年の未解決問題でした。この結果は、コンパクトな共有表現を使用することでこれを回避します。この結果は、縮約中にラムダ項を評価するために必要なスペースの量が項のサイズに比例しないことを明らかにしています。スペース複雑性の適切な尺度が何であるかは現在わかっていません。[ 37 ]
不合理なモデルが必ずしも非効率を意味するわけではありません。最適削減では、同じラベルを持つすべての計算を 1 つのステップで削減し、重複作業を回避しますが、与えられた項を正規形に削減するための並列 β 削減ステップの数は、項のサイズに対してほぼ線形です。これは、任意のチューリング マシンをチューリング マシンのサイズに線形に比例するサイズでラムダ計算にエンコードできるため、妥当なコスト尺度としては小さすぎます。ラムダ項を削減する真のコストは、β 削減自体によるものではなく、β 削減中の redex の重複の処理によるものです。[ 38 ]最適削減の実装が、正規形への最左端ステップ数などの妥当なコスト モデルに関して測定した場合に妥当かどうかはわかりませんが、ラムダ計算の断片については、最適削減アルゴリズムが効率的であり、最左端と比較して最大で 2 乗のオーバーヘッドがあることが示されています。[ 37 ]さらに、BOHM プロトタイプによる最適縮約の実装は、純粋なラムダ項においてCaml Light と Haskell の両方を上回った。[ 38 ]
ピーター・ランディンの1965年の論文「ALGOL 60とチャーチのラムダ記法との対応関係」 [ 39 ]で指摘されているように、逐次手続き型プログラミング言語はラムダ計算の観点から理解することができ、これは手続き的抽象化と手続き(サブルーチン)適用の基本的なメカニズムを提供する。
例えば、Pythonでは「square」関数はラムダ式として次のように表現できます。
(ラムダx : x ** 2 )上記の例は、第一級関数に評価される式です。この記号はlambda、パラメータ名のリスト(xこの場合は単一の引数)と、関数の本体として評価される式 を指定して、匿名関数を作成しますx**2。匿名関数は、ラムダ式と呼ばれることもあります。
Pascalをはじめとする多くの命令型言語では、関数ポインタの仕組みを通して、サブルーチンを他のサブルーチンへの引数として渡す機能が長らくサポートされてきました。しかし、関数ポインタは関数が第一級データ型となるための十分条件ではありません。なぜなら、関数が第一級データ型となるのは、実行時にその関数の新しいインスタンスを作成できる場合に限られるからです。このような実行時の関数作成は、Smalltalk、JavaScript、Wolfram Language、そして最近ではScala、Eiffel(エージェントとして)、C#(デリゲートとして)、C++11などでサポートされています。
ラムダ計算のチャーチ・ロッサー特性は、評価(β還元)を任意の順序で、並列でも実行できることを意味します。これは、さまざまな非決定論的な評価戦略が有効であることを意味します。しかし、ラムダ計算には並列処理のための明示的な構成要素がありません。ラムダ計算にフューチャーなどの構成要素を追加することは可能です。通信と並行性を記述するために、他のプロセス計算が開発されています。
ラムダ計算の項が他のラムダ計算の項、さらには自分自身に対しても関数として作用するという事実は、ラムダ計算の意味論に関する疑問を生じさせた。ラムダ計算の項に意味のある意味を割り当てることはできるだろうか?自然な意味論としては、関数空間D → Dと同型な、それ自身に対する関数の集合Dを見つけることである。しかし、 DからDへのすべての関数の集合はDよりも大きな濃度を持つため、濃度制約により、 Dが単一集合でない限り、そのような非自明な Dは存在し得ない。
1970年代に、ダナ・スコットは、連続関数のみを考慮すれば、必要な性質を持つ集合または領域Dが見つかることを示し、ラムダ計算のモデルを提供した。 [ 40 ]
この研究は、プログラミング言語の表示的意味論の基礎も形成した。
これらの拡張機能はラムダキューブに含まれています。
これらの形式体系は、ラムダ計算の拡張であり、ラムダキューブには含まれていない。
これらの形式体系はラムダ計算の変形である。
これらの形式体系はラムダ計算に関連している。
この記事の一部は、FOLDOCの資料に基づいており、許可を得て使用しています。