手続き型との違い


let と 手続き型言語の局所変数は似ています。 letを用いた純粋な関数型プログラムと、 手続き型言語で書かれたプログラムの大きく異なる点は、 代入があるかないかという点です(*)。 代入操作があるかないかは大きな違いで、 必要とする実行モデルが異なってきます。

まずは、純粋な関数型言語(以下、単に関数型言語という)の実行モデルを説明します。 関数型言語では変数は値に直接束縛されます。 また、束縛関係は変化しないため、 同じ環境の中では変数の値は一定で変更されることはありません。つまり、

	      束縛(環境)  
	変数     ->       値
と図示できます。 また、変数の値は変化しないので、 プログラムのある点で計算した値を別の個所に伝えるには、 変数の導入時に初期値として設定するか、 もしくは 関数の引数や返り値として値を渡すことになります。

このため、プログラミングの自由度は制限されます。 しかし、データの流れがはっきり定義されるので 「プログラムのどこかで勝手にある場所の値を変えられてしまった」というタイプの 厄介なバグが最初から禁止されています。 また、データの流れがはっきりしていることで処理系による解析が容易になるので、 高度な最適化を期待できます。

一方、手続き型言語の場合は、どのような実行モデルが必要となるのでしょう? 手続き型言語の説明では、よく変数を箱に例えた説明がなされます。 変数名によって特定の箱が指し示されており、箱の中にはなんらかの値が入っている、 という説明です。 変数への読み出し・書き込み操作により、 箱からの値の取りだしたり、箱に値を納めたりするという具合いです。 これをモデル化したものがストア(store)です。 ストアというのは、場所(location)からその値への写像(つまりは対応関係)のことです。 場所を指定することで、その場所の値を読み出すことができます。 また、書き込み操作によって、指定場所の値が更新された、新たなストアに変化します。

注意してもらいたいのは、変数自身が直接値に束縛されているのではないという点です。 変数は、場所に束縛されており、 その場所に格納されている値が変数の値となるわけです。 つまり、以下のような関係になります。

	      束縛(環境)        場所と値の対応(store)
	変数     ->       場所            ->          値
変数の代入操作では、 変数の場所への束縛は保たれたまま、 ストアつまり場所と値の対応関係が変化することで 変数の値が変更されていきます。

なお、 場所を値として使用することもあります。 つまり、ポインタ(pointer)や参照(reference)などと呼ばれるものです。 手続き型言語では、 大域変数やポインタ等をもちいて同じ場所をみることによって、 その場所の値の変更をプログラムのいろいろな場所から共有することが出来ます。 よって、プログラムのある点で計算した値を、 store を通じてプログラムの別の点に一見分からないように伝えることもできます。

このため、プログラミングの自由度は大きくなるのですが、 反面、 プログラムの挙動を理解するために ストアがどのようになっているかを理解する必要があり、 プログラムの理解や解析が困難になりがちです。 なぜなら、ストアがどうなっており、どう変化していくかを理解しなくては、 プログラムの意味を正しく理解できないからです。

関連した話は こちら(関数型言語とメモリ)


98.12.08/ Tomio KAMADA: kamada@seg.kobe-u.ac.jp