練習問題3.5

 この問題は、練習問題3.4で定義したオブジェクトおよび擬似関数を用いて解く。それらの擬似関数などを以下に示す。


 

束縛

 順序対(var, val)で表す。ここで、varは変数、valは値である。

 

部分環境

 順序対(A, R)であり、[]による記法で表す。ここで、Aは同変数の束縛のクラス、RはA上の順序関係である。また、[]には宣言の新しい順に束縛が並ぶ。

例: x = 3, x =1の順で宣言がなされたときの変数xの部分環境
[(x, 1), (x, 3)]

 

環境(全体環境)

 部分環境の和クラスであり、[]と⊕とによる記法で表す。

例: 変数xと変数yとが宣言された環境
[(x, 1), (x, 3)]⊕[(y, 1)]

 

宣言関数𝔇

 環境Envと宣言による束縛のクラスとから新しい環境Env'を選ぶ。

Env' = 𝔇(Env, {(var₀, val₀), ..., (varₙ, valₙ)})

 

評価関数𝔈

 式exprを環境Envで評価する。

val = 𝔈(expr, Env)

 

 


 以下の答案では、本問の2種類の宣言のうち、xとyとを同時に宣言するものを同時宣言と呼び、xを宣言した後にyを宣言するものを逐次宣言と呼ぶ。また、初期の大域環境Envを空[]とする。

 

同時宣言

 同時宣言後の大域環境Envₚ'は、

Envₚ' = 𝔇(Env, {(x, 𝔈(e₁, Env)), (y, 𝔈(e₂, Env))})
= {(x, 𝔈(e₁, Env)), (y, 𝔈(e₂, Env))} ⇢ []
= [(x, 𝔈(e₁, Env))] ⊕ [(y, 𝔈(e₂, Env))]

となる。

 

逐次宣言

 xについての宣言後の大域環境Envₛ'は、

Envₛ' = 𝔇(Env, {(x, 𝔈(e₁, Env))})
= {(x, 𝔈(e₁, Env))} ⇢ []
= [(x, 𝔈(e₁, Env))]

となり、yについての宣言後の大域環境Envₛ''は、

Envₛ'' = 𝔇(Envₛ', {(y, 𝔈(e₂, Envₛ'))})
= 𝔇([(x, 𝔈(e₁, Env))], {(y, 𝔈(e₂, [(x, 𝔈(e₁, Env))]))})
= {(y, 𝔈(e₂, [(x, 𝔈(e₁, Env))]))} ⇢ [(x, 𝔈(e₁, Env))]
= [(x, 𝔈(e₁, Env))] ⊕ [(y, 𝔈(e₂, [(x, 𝔈(e₁, Env))]))]

となる。

 

 以上から相違点をまとめる。

答え: 同時宣言と逐次宣言とは、yに束縛される値が式e₂を評価したものである点は同じだが、その式e₂を評価する大域環境が異なる。
 より詳細には、同時宣言では初期の大域環境で式e₂が評価される。一方、逐次宣言では、変数xと式e₁の値との束縛が初期の大域環境に追加された大域環境で式e₂が評価される。

 


 

感想

正解

 どうにも正解の確信が持てずモヤモヤする。原因はlet定義にあるのだろう。

 いまさらだが、let定義は式ではない。式ではないのなら何なのだろう。テキストでは、OCamlのプログラムの実行とは、式を評価して値を求めることだ、との説明がなされていた。

 それならば式でないlet定義は、少なくとも"普通"のプログラムの実行ではなく、何か"特別"な動作なのだろうか。そういえば、let定義が対象とする大域環境も、OCamlでは明示的に扱えない。このあたりがモヤモヤの原因か。

 つまり、問題の2つのlet定義は違いがあるものの、その違いは外部には現れない。現れるのはプログラムが実行され、let定義されたyが評価されたときだが、そこまでは問題に含まれていない。なんのことはない、目に見える明確な違いを示してスッキリしたいのだが、それができないからモヤモヤしていただけか。

 

let定義

 そもそもlet定義は必要なのだろうか。変数の定義は、let式があればこと足りるように思える。なぜなら、let定義だけ行うプログラムというのはありえない。必ずlet定義した変数を評価するはずだ。ならば、その評価のときにlet式を使えばよいのだ。もっといえば、プログラムの全部を1つの大きなlet式で覆ってしまえばよい。

 計算という点、OCamlでいうところの式の評価という点からは、let定義は不要に思える。必要となるのは、対話式のインターフェイスなどでだろう。let定義は、プログラマの便宜を図るために、実用性の観点から導入された仕組みなのだろうか。

 また、式の評価(値)が文脈に左右されるのはどうにも気持ちが悪い。例えば、関数fを、

 let f x = (f x) + 1

と定義した場合、再定義の回数によって、

 f 0 = ?

が異なる。これでは、対話式でうっかりミスして再定義した場合には、原因不明のバグに悩まされることになる。せめて、大域環境をダンプできればチェックのしようもあるのだが。

 とはいえ、この問題を解くまでは、何の疑問もなく使ってきた。気にしなければ、便利な仕組みなのだが。

 

 

 


 

上記は、プログラミングinOCaml,五十嵐淳についての感想および練習問題の答案である。著作権を侵害してしまうことがないように問題文は載せていない。ただし、問題文中において、ごく短い文であって、ありふれた表現であり、それのみでは問題としての意味をなさないようなもの(例えば、"- - 1"など)は、著作物にあたらないと判断し、記述している。