fortran66のブログ

fortran について書きます。

function の side-effect について :追記あり

Fortran では、function の副作用は嫌われていて、文法的には許されていますが、道徳的に禁止になっています。Fortran95 以降では pure 指定子が出来たので、常に pure というのが行儀のよさだと思います。副作用のある場合は、サブルーチンを使います。

この点については、FORTRAN II で SUBROUTINE と FUNCTION が導入された時のマニュアルにも引数の値の変更はしないように書かれてあります。(厳密に言えば、昔の FORTRAN では変数は全て静的に確保されているので、静的変数に前回の履歴が残るという形で意図せずとも副作用がありますがw)

f:id:fortran66:20171004040611p:plain
FORTRAN II for the IBM 704 Data Processing System : Reference Manual | 102653989 | Computer History Museum
(p. 28)
fortran66.hatenablog.com

また、Fortran95 の impure 指定子に類する ABNORMAL FUNCTION 指定子が、1960年ごろの UNIVAC 1107 FORTRAN IV にあったそうです。
fortran66.hatenablog.com

Fortran で function の副作用が嫌われている理由は、最適化のためだと思います。副作用がなければ、関数は sin(x) + sin(x) が 2*sin(x) とまとめられるように、f(x) + f(x) を 2 * f(x) と出来ます。また FORTRAN では、式の項の評価順序が決まっておらず、コンパイラが自由に並べ替えて最適化できるので、副作用のある関数が項として複数現れる式では、結果が不定になってしまいます。

なお Fortran の組み込み関数や組み込みサブルーチンでもこの原則は守られていて、他言語では関数になっている乱数や時間もサブルーチンとして与えられて、引数で値を得るようになっています。

Wirth の場合

一方 N. Wirth の著述をみると PROGRAMMING IN MODULA-2 では、13章(p.52)に副作用のある関数の例が出てきて、引数の変更を含めて、特に禁止するようなことを書いていません。

Programming in Modula-2 (Monographs in Computer Science)

Programming in Modula-2 (Monographs in Computer Science)

しかし、のちの Programming in Oberon: Steps Beyond Pascal and Modula では 6.6.1 Side-effect の節で、副作用を好ましくもなく、良いプログラミング・スタイルでもないとしています。しかし、例として疑似乱数を挙げて、限定的使用も認めています。しかしその場合でも、許されるのは静的な変数を通じての依存性で、引数への副作用は戒めています。

Programming in Oberon: Steps Beyond Pascal and Modula (ACM Press)

Programming in Oberon: Steps Beyond Pascal and Modula (ACM Press)

f:id:fortran66:20171004032527p:plain

メモ帳:Wirth は上記 MODULA-2 本で、一般の関数名は名詞、論理型を返す関数名は形容詞、サブルーチン名は動詞にすることを勧めています。(上記 MODULA-2 本は、ほぼ同内容のものを Oberon 版にも書き直しており(上記本とは別のもの)、多分元々は PASCAL 版もしくは ALGOL 版で、内容は共通しているものと推測します。)

追記:
PASCAL report 初版では、グローバル変数も引数も代入するのはまかりならぬ(PURE であれ)となっていますが、改訂版では、その記述がごっそり削られています。 

f:id:fortran66:20171004213925p:plain
N. Wirth, The Programming Language Pascal (Revised Report) Acta INformatica, 1 (1971), 35-63.
http://oberoncore.ru/_media/library/wirth_the_programming_language_pascal.pdf

f:id:fortran66:20171004213641p:plain
N. Wirth, The Programming Language Pascal (Revised Report) (1972)
http://www.eah-jena.de/~kleine/history/languages/Wirth-PascalRevisedReport.pdf

Kernighan の場合

元々 C 言語は サブルーチンと関数の区別がなく、かつ関数の返り値をエラーコードとして利用して、引数の方の副作用で結果を返すことを常用しているので、関数の純粋性の概念は薄いと思います。

RATFOR を使った Software Tools でも、しょっぱなから getc(c) 関数を定義して、返り値のほかに引数にも同じ値を入れて返しています。Kernighan は、これは文法に違反していないと本文でわざわざ断っていますが、FORTRAN 的には行儀がよくない作法になります。

なぜこのようにしたかといえば、DO WHILE () 構造のためです。DO WHILE 構造は、Fortran90 から落選しそうになるくらい Fortran では微妙な構文です。Fortran では代入をはじめ実行文は値を持たないので、DO WHILE 構文の条件式には代入文や実行文を書けせん。したがって、DO WHILE ( (c=getc()) /= EOF) のような、1文字読んで変数に代入してその値が EOF かどうか調べる、といった書き方が出来ません。このようなスタイルは C 言語系のプログラムではよく見ますが、Fortran では使えません。DO WHILE 構文の使い勝手があまり良くないのです。

Software Tools の巻頭一番で、副作用付きの関数 getc(c) を導入して DO WHILE (getc(c) /= EOF) と getc 関数の返り値で EOF のチェックをするとともに、関数引数の副作用で変数 c に返り値と同じ値を代入するしかなくなっています。

これは、DO WHILE 構文をすてて、素直に DO..END DO の無限ループを EXIT 命令で抜ける形にすれば解決できますが、脱出が LOOP の中途から起こることになります。REPEAT..UNTIL 型の構文は滅多に使われることがないので、Fortran では採用されていませんから、個人的には更に一歩進んで DO WHILE 構文も捨てた方が良かったろうと思います。

Software Tools

Software Tools

ソフトウェア作法

ソフトウェア作法