プログラミングの基礎理論 第2回 言語の帰納的な定義
目次
はじめに
Twitterにてこのようなツイートを見かけたので、内容を確認してみた。
これまでインターネットや書籍で軽く触れられてきた内容に切り込んでいくものだったので、理解を深めることができた。
その内容をまとめていく。
概要
言語は文法で定義される。
例えば、MLの一部の式は以下のように定義される。
<exp> ::= <id> // 変数
| <n> | <S> | true | false // 自然数(n), 文字列(S) その他定数
| fn <id> => <exp> // 関数式
| <exp> <exp> // 関数適用
| ...
プログラミング言語の理論ではより抽象的な定義を行うため、変数や定数を「与えられた集合」とし、これを代表するものをメタ変数
と呼ぶ。
ラムダ式では以下の集合とメタ変数を導入する。
- 変数(Var)に対するメタ変数を
x
とする - 定数(Const)に対するメタ変数を
c^b
(c of b)とする- ※ b はint等の型を明示するためのもの
構文自体に対してもメタ変数を導入できる。
- ラムダ式の集合(lambda)に対するメタ変数を
e
とする- ML式における非終端記号
<exp>
に相当する
- ML式における非終端記号
BNF記法では以下のように表される。
e ::= x | c^b | (lambda x.e) | (e e)
これはMLの一部の式に示した定義と同等であることが分かる。
このような定義を抽象構文(abstract syntax)の定義と呼ぶ。
ここで抽象とは、語彙解析や構文解析の詳細について了解されていることを示す。
なぜ抽象構文を用いるか。
言語の型等の性質の理論的な分析を行うため、と言える。
また分析の対象となるのは、機能法等で分析可能な集合である、と言える。
ここで以下のBNFを思い出そう。
e ::= x | c^b | (lambda x.e) | (e e)
上記eはラムダ式の集合とみなせる。この集合をΛとする。
するとラムダ式の集合e(またはΛと表現)の条件が見えてくる。
- Λはxが代表する集合Varを含む
- Λはcが代表する集合Constを含む
- もしΛにeが含まれるのであれば、eを使った式もΛに含まれる
- もしe1及びe2がΛに含まれるのであれば、(e1, e2)もΛに含まれる
- Λは以上の要素のみを含む
またBNFから、Λを生成する以下の規則も見えてくる。
つまり先程のBNFは、Λの要素を生成する以下の操作であると言える。
- xと書いた場合、xが代表する集合VarをΛの要素に加える操作である
- c^bと書いた場合、c^bが代表する集合ConstをΛの要素に加える操作である
- もしeが集合lambdaに含まれるなら、(lambda x.e)と書いた場合、これをΛに加える操作である
- もしe1,e2が集合lambdaに含まれるなら、(e1 e2)と書いた場合、これをΛに加える操作である
また以上のことから、ラムダ式の集合は以上の要素生成を(無限に)繰り返して得られる集合であると言える。
集合の帰納的定義
動画ではここからスタートする内容である。
さて、前述したラムダ式の集合における条件や規則について、それらが数学的に定義可能かを見ていく。
まず、数学的定義を行うために必要な道具(集合に関する記法)を整理する。
- A,A1…,An を集合とする
- 集合に対して以下の集合演算が可能である
- A1 ⊗ …Ax = {(a1,…,an) | ai ∈ Ai (1 ≤ i ≤ n)}
- これはA1,…Anの直積を表す
- An = A ⊗ … ⊗ A
- これは集合Aのn次の直積を表す
- A1 ⊗ …Ax = {(a1,…,an) | ai ∈ Ai (1 ≤ i ≤ n)}
- 関数を代表するメタ変数fに対して、その定義域を dom(f) と表記する
- Xがdom(f)の部分集合である時(すなわちX ⊆ dom(f)の時)、集合{f(x) | x ∈ X}をf(X) と表記する
- fのXへの制限すなわち定義域をXに制限した集合{(a, b) | (a, b) ∈ f, a ∈ X}で表される関数を、f|xと表記する
- f|dom(f){x}すなわちXへの制限を取り除いたものをf|¬xと表記する
- 変数x1〜xnと対応する値v1〜vn (xi ≠ xj, 1 ≤ i ≤ j ≤ n)に対して、f{x1:v1,…,xn:vn}は新たな関数f’を表す
- f’の定義域dom(f’)について、dom(f’) = dom(f) ∪ {x1,…,xn} となり
- 且つf’の値について、f’(y) = f(y) (y ≠ xi, 1 ≤ i ≤ n) または vi (y = xi)となる
- xiはdom(f)の定義であっても良い、そうでなければviとなるという意味
次に、全体集合Uを置き、集合Fを f ∈ Un → U つまり、Uのn次の直積からUへの関数の集合と定義する。
この時nをfのランクと言いrank(f)と書く。
また、ランクがnであるFの要素をfr(n)と書く。
ここで、Uに対する部分集合X(X ⊆ U)に対して、{fr(n) (x1,…,xn) | xi ∈ X, fr(n) ∈ F} となる集合をF(X)と書く。
つまりF(X)は、関数集合Fに属するfについて、ランクnにx1〜xn(xはXの要素)を与えfに適用した結果を集めた集合である。
さらにF(X) ⊆ X の時、すなわちXの要素(xi等)をfに適用した際にその結果がXの範囲に留まる時、「XはFに関して閉じている(Closed under F)」と言う。
帰納的閉包
部分集合C(C ⊆ U)を与えられた集合とし、CのFに関する帰納的閉包を「集合Cを含みFに関して閉じている集合の最小のもの」と定義する。またこれを Ind(C, F)と書くことにする。
ここで最小とは、集合の包含関係(例えば A ⊆ B)に関して最小のもの、という意味である。
この講義で言いたいことは「BNFを含む再帰的な規則で定義される集合は、帰納的閉包である(帰納的閉包で表される)」ということである。
帰納的閉包の性質1
そもそもInd(C, F)つまり集合Cを含み関数集合Fに関して閉じている最小の集合というものは存在するのだろうか(直感的には存在するとは思うが、確認することが大事だ)。
そこでXを 空でない集合の集まり(集合)とし、以下の演算を定義する。
∩ X = {a | Xの要素(実体は集合)である全てのY(∀Y ∈ X)について a ∈ Y}
つまり上記の演算は、Xの全ての部分集合における共通集合を得ることになるため、必然的に最小の集合を得る演算となっている。
さて、Uの部分集合C(C ⊆ U)且つ F(U) ⊆ U から、U自身はCを含み且つFに関して閉じた集合全体の集合はUの要素を含み空ではないことが分かる。
また、空ではない集合の集合Xについて ∩ X の演算を定義したことから、F(X)すなわちXがFに関して閉じている場合、∩ X においてもFに関して閉じていると言える。
したがって Ind(C, F)は確かに存在し以下のように定義できる。
Ind(C, F) = ∩{V | V ⊆ U, C ⊆ V, F(V) ⊆ V}
つまり Uの部分集合V(V ⊆ U)は、Uの部分集合Cが含まれ且つFに関して閉じている全てのものの集まりであり、これに対して ∩ をとったものである。∩ をとるというのは、Vにおける最小の集合を得ることであり、ここではCが最小の集合であるため、Ind(C, F)は確かに存在することが分かる。
帰納的閉包の性質2
ここでは、Ind(C, F)にどのような要素が含まれているかを確かめていく。
自然数で添字付けられた無限列(集合系列) {Xn | n = 0,1,…,n} を以下のとおり定義する。
X0 = C
Xi+1 = Xi ∪ F(X) (0 ≤ i)
上記のX0は定数Cを表す。
Xi+1は、1つ前の値であるXiとF(Xi)(つまり関数集合Fに属するfにXiを適用して得られる全ての値)の和集合を表す。
つまり、上記の漸化式で表される集合系列は、添字の数を大きくなる毎にその集合自体も大きくなる。
この集合系列のもとで、Ind(C, F)は以下のように定義できる。
Ind(C, F) = ∪ {Xi | 0 ≤ i}
(それはそうか、という感じもするが、証明が必要だ)
性質2の証明
性質2を証明するにはどのようにすれば良いか。以下の2つを証明すれば良い。
- Ind(C, F) ⊆ ∪ {Xi | 0 ≤ i}
- ∪ {Xi | 0 ≤ i} ⊆ Ind(C, F)
1.については、次のステップで証明できる。
- ∪ {Xi | 0 ≤ i}がFに閉じていることを示す
- fr(n)をFの任意の要素とし、∪ {Xi | 0 ≤ i}n (n次の直積)の任意の要素(
実際はXiで表される要素の組)をxとする - そのようなxをとった時、あるkが存在して x ∈ (Xk)nであることを示す
- するとfr(n) ∈ Xk+1 ⊆ ∪ {Xi | 0 ≤ i}である
- 同様にF(∪ {Xi | 0 ≤ i}) ⊆ ∪ {Xi | 0 ≤ i}である
- よってInd(C, F) ⊆ ∪ {Xi | 0 ≤ i}である
※ 2,3のステップが微妙に整理できていないかも?
2.については、次のステップで説明できる。
- 定義よりX0 = C ⊆ Ind(C, F)である
- Xi ⊆ Ind(C, F)と仮定する
- Ind(C, F)はFに閉じているのだから、F(Xi) ⊆ Ind(C, F)である
- 定義よりXi+1 = Xi ∪ F(Xi)であるから、Xi+1 ⊆ Ind(C, F)である
- よって∪ {Xi | 0 ≤ i} ⊆ Ind(C, F)である
帰納的閉包によるラムダ式の理解
この講義で言いたいことは「BNFを含む再帰的な規則で定義される集合は、帰納的閉包である(帰納的閉包で表される)」ということであった。早速見ていこう。
全体集合U(例えば構文木全体からなる集合等)を置き、ラムダ式の集合Λを帰納的閉包として定義する。
なおこの時Λは全体集合Uの部分集合になっている、
さて集合Varに対するメタ変数x(x ∈ Var)について、e(ラムダ式の集合Λに対するメタ変数)から (lambda x.e)を作る関数をf1lambda x、e1及びe2から(e1 e2)を作る関数をf2appとすると、関数集合Fは以下のように定義できる。
FΛ = {f1lambda x | x ∈ Var} ∪ {f2app}
するとラムダ式の集合Λは、以下のとおり定義できる。
Λ = Ind(Var ∪ Const, FΛ)
つまりX0に相当するVar及びConstの和集合から始まり、それらを用いて作成できる関数を要素に持つ集合Fに関して閉じている最小の集合を示している。
BNFを帰納的閉包の文脈で見直してみる
Λ = Ind(Var ∪ Const, FΛ) = ∩ {V | V ⊆ U, (Var ∪ Const) ⊆ V, FΛ(V) ⊆ V}
と考えると、Λは次の制約を満たすものであると定義できる。
- (Var ∪ Const) ⊆ V よりΛは集合Varと集合Constを含む = (Var ∪ Const) ⊆ Λ
- FΛ(V) ⊆ V よりFΛ(Λ) ⊆ Λつまりf1lambda x及びf2appに関して閉じた集合である
- 上記を満たす最小の集合である
つまり、
- Λはxが代表する集合Varを含む = Var ⊆ Λ である
- Λはcが代表する集合Constを含む = Const ⊆ Λ である
- もしΛにeが含まれるのであれば、eを使った式もΛに含まれる = もし e ⊆ Λ ならば (lambda x.e) ⊆ Λ である
- もしe1及びe2がΛに含まれるのであれば、(e1 e2)もΛに含まれる = もしe1 ⊆ Λ 且つ e2 ⊆ Λ ならば (e1 e2) ⊆ Λ である
- Λは以上の要素のみを含む ← これがはっきりとした!
と言える。
同様にΛの要素を生成する操作という文脈においても別の見方ができる。
Λ = Ind(Var ∪ Const, FΛ) = ∪ {Xi | 0 ≤ i}
- (Var ∪ Const)の要素を追加する
- 全てのx, y ⊆ Λ に対して、f1lambda x及びf2appを追加する
つまり
- xと書いた場合、xが代表する集合VarをΛの要素に加える操作である
- c^bと書いた場合、c^bが代表する集合ConstをΛの要素に加える操作である
- もしeが集合lambdaに含まれるなら、(lambda x.e)と書いた場合、これをΛに加える操作である
- もしe1,e2が集合lambdaに含まれるなら、(e1 e2)と書いた場合、これをΛに加える操作である
- Λは異常の要素の生成を(無限に)繰り返して得られる集合である ← これがはっきりした!
と言える。