FORTRAN の誕生日ついでにもう一つ。
時々 FAQ のように「なんでループ変数に I とか J とか使うの?」という話を聞きます。
これ、知っている人は知ってますね。FORTRAN がそうだったから、というのが答えです。
でも、こないだ Twitter で見た疑問は「なんで FORTRAN は I から N が整数なの?」でした。
…そうきたか。そこを疑問に思ったことはなかった。
一応、疑問の主は「Integer とか Number なのだろうけど」と言う部分まで理解しています。
先に書いた FORTRAN の誕生日は、実はこの疑問が面白いと思ったために調べていて気付いたのでした。
最初のマニュアルを調べていたら、日付が近かったので「あ、これ記念日ネタだ」と。
で、答えを先に書けば、マニュアルには「I~N が整数」となっているだけで、理由は書かれていませんでした。
策定に2年かけているから、当初は何か理由があったのだろうけど、マニュアルに書く必要は感じられなかったのでしょうね。
FORTRAN が完成した 1957年には、PRIMER …つまり、FORTRAN プログラミング初心者向けの教則本が作られています。
こちらも理由は無しで「I~N が整数」というだけ。
IBM 650 にも FORTRAN があって…というか、IBM は作ってないのにカーネギー工科大学(現在のカーネギーメロン大学)が勝手に移植したようで、IBM が再配布しています。
勝手に作ったものなので一部互換性が無いようで FOR TRANSIT という名前です。
でも、再配布に際して IBM が作ったマニュアルは、704 の物とほぼ同じ。
つまり、ここでも理由は書かれていません。
FORTRAN のマニュアルなどには記述が無いので、以降は推測にすぎません。
まず、FORTRAN は科学計算用であったことに留意が必要です。
科学者が使う「変数名規則」をそのまま使う場合、特に説明はいらないのです。
適当な変数を使って式を立てる際、整数を I とするのはよくあることです。
Integer (整数、という意味の英語)の先頭の文字ね。
#通常、式を立てる際は変数は小文字筆記体で書かれるが、IBM 704 は 6bit で文字を表現するため、大文字しか使えなかった。
整数が複数必要なら、 I J K …とアルファベットを後ろにずらして順番に使います。
アルファベットの成立の歴史の中で、元々 J は存在せず、I から派生したものでした。
この点でも、I と J を同じように使うのに、それほど違和感はありません。
また、科学者は「自然数」の意味で、N を使います。
自然数っていうのは「1以上の整数」の意味ね。
こちらは、Nature number(自然数の意味の英語)の N です。
複数必要な場合、N M L …とアルファベットを前にずらして順番に使います。
なぜ前にずらすのかと言えば、おそらく理由は2つ。
1つ目は、後ろにずらした O よりも、 M の方が N に似ている感じがするから。
「同じ意味である」ことを示すのは重要なので、似ている気がするというのは大事です。
それに、NuMber (数値) という単語のイメージともあいます。
2つ目は、後ろにずらした O (オー) には、「原点」という別の意味があるのです。
O (オー)は数字の 0 (ゼロ) とよく似ています。
複数の数値をまとめて扱う場合、それらの数値すべてが 0(ゼロ) の点を O(オー)で示して特別視することがあります。
以上の理由で、科学者は整数に I J K ... を、自然数に N M L ... を使うのです。
ところで、FORTRAN の変数は、実数型と整数型しかありません。
自然数を表現したいなら、整数型を使うことになります。
FORTRAN のプログラムに於いて、自然数と整数は区別されないのです。
そこで、変数名の最初の1文字が I J K L M N で始まる変数は整数型、それ以外は実数型、と決め打ちになります。
これが、科学者にとって使いやすい言語仕様なのです。
型が変数名によって決まるので、変数の宣言などは不要で、いつでも使いたいときに変数を使えばよい、という形式でした。
#最初の FORTRAN では、変数名は6文字までの長さが許されていました。
…これ、思わぬバグの原因でした。
非常に有名な例を挙げましょう。
FORTRAN では、ループに「DO」と言う命令を使います。次みたいな感じ。
DO 100 I=1,10
WRITE (*,*) I
100 CONTINUE
行番号 100 に書かれた CONTINUE までの間を、I が 1 から始め 10 になるまで(増分1で)繰り返せ、という命令です。
(WRITE …は、変数 I を印字する命令ですが、ここでは気にする必要はありません)
当時、プログラマー(アルゴリズムを考案する人)と、コーダー(FORTRAN でアルゴリズムを記述する人)、パンチャー(コーダーの記述をパンチカードに打ち込む人)は別でした。
そして、上のプログラムをパンチャーがこう打ち込むのです。
DO100I=1.10
WRITE(*,*)I
100 CONTINUE
空白が無くなったことは問題ではありません。
FORTRAN の仕様で、空白は全く無視されました。パンチカードを無駄にしないように、詰め込んで書けるのです。
でも、空白以外に変わった場所があります。最初の行で、カンマ (,) がピリオド (.) に打ち間違えているのです。
すると、DO100I という、6文字以内の規定に正しく従った変数名に、1.10 という、変数の型に正しく沿った実数を代入してしまいます。
規定どおりなので、動作としては全くおかしくありません。
100 行目には CONTINUE という命令が書かれています。ループの終わりです。
「ループが始まってないのに終わりを書いてあればエラーになってくれるのでは」…と、今のプログラマーなら期待してしまいますが、そうはなりません。
実は、CONTINUE という命令は「明示的に行番号を作り出す」ためだけに存在する命令で、「何もしない」と決まっているのです。
だから、DO 命令が存在しないのに CONTINUE がある、という点でもエラーは出ません。
結果として、プログラマーが考えたアルゴリズムは、 カンマ (,) とピリオド (.) を間違えるというミスにより、全く違う動作に変換されてしまい、エラーとして検出されません。
この話、昔は「NASA がこれで金星探査機を失った」とされていたのですが、今は否定されていますね。
(NASA でこのバグが出たのは事実らしい。…NASA に限らず、このバグは良くあったみたいだけど)
上のバグがなぜ出るかと言えば、2つあって
1) 宣言もせずに変数が使えるのが良くない。
2) ループの「はじめ」と「おわり」をセットにしていないので、おかしくなったことに気付けない。
の2つです。
この反省から、ALGOL 以降の言語では、「はじめ」から「おわり」までのブロックを示すことと、変数を宣言することが重視されています。
ブロックは、まぁいいです。実際便利なので。
でも、変数をわざわざ宣言しないと使えないって、面倒くさいようにも思います。
で、今の言語では、変数の宣言が不要、というのも流行しています。面倒がなくていいね!
…そしてまたバグが出る。
変数の宣言が不要、というのは大抵は軽量言語 (Lightweight Language : 略称 LL) と呼ばれるものの特徴で、変数に「型」がありません。
整数型なんて知らない。全部実数でいいじゃん。
文字列型はあるけど、必要に応じて文字と数値は自動変換するよ。変数には気にせず突っ込んじゃえ。
特に型が無いんだから変数宣言なんていらないじゃん。好きな時に使えばいいよ。
…これ、非常に楽なんですよね。実際僕も現在は主に PHP / Javascript プログラマです。
#昔は Perl が好きだったし、そもそもゲームプログラマ時代はCもアセンブラも使ってましたよ。
今でも使えると思うけど、易きに流れて楽している感じ。
さて、LL の始祖ともいえる awk は、フィルタを「言語的に拡張したもの」だったので、バグが出るほど大きなプログラムを作るのも苦手でした。
でも、awk を拡張した perl では、かなり大きなプログラムを作れる。
にもかかわらず、変数を宣言しないでも良いので、うっかり変数名を間違えた時に気付かない。
ところで、C言語では、変数は「大域変数」と「局所変数」に別れていました。
大域と言うのは、大きなプログラムの全体、どこからでも参照できるもの。
プログラムと言うのは、内部では小さなプログラムの寄せ集めになっているのだけど、「局所変数」は、この小さなプログラムの中でしか参照できないもの。
小さなプログラムの中で変数を宣言すると、局所変数になるようになっていました。
そして、perl も同じ方法です。
小さなプログラムの中で宣言すれば、局所変数になります。
逆に言えば、うっかり間違えた変数名は、宣言されていないので「大域変数」になります。
うっかりミスがプログラム全体に影響を及ぼしてしまう、ということ。
…やっぱ問題があると思ったようで、Perl 5 以降では、変数を宣言しないとエラーになる、というモードが追加され、そのモードを使うことが推奨されています。
あぁ、やっぱ変数の宣言って、面倒でもした方がよかったね、という流れ。
ところが、perl 以降に現れた Javascript でまた、変数の宣言が不要になります。
局所変数の定義方法など、perl と同じ構造。問題の再発です。
こちらも、同じ問題は同じ対処で…と、変数を宣言しないとエラーになるモードが追加されます。
なんと、このモード切替方法まで perl とほぼ同じ。
さらに後に作られた PHP 。
同じ轍は踏まないけど、面倒な変数宣言はやっぱ使いたくない、と思ったのか、「宣言しない限り局所変数」となりました。
小さなプログラムの中で「これは大域変数だよ」と宣言したものだけが、大域変数として使えます。
なるほど、これなら、うっかりミスしても影響は局所的です。
根本的な解決方法にはなっていませんが、perl や Javascript よりは良さそう。
でも、変数宣言したくないものだから、「URL で変数を渡したら、自動的に大域変数になる」なんて機能もあったのですね。
これが大問題で、PHP の最大のセキュリティホールとして、大きな仕様変更を伴いながら、現在では基本的に機能が無くなりました。
…過去との互換性を考えて、まだこの機能を「使おうと思ったら使える」のですけどね。
セキュリティ的には良くないのだけど、過去にやっちゃったので、今更やめにくい。
(近い将来廃止する、とずっとアナウンスしていますが)
今では、やはり「変数の宣言必須モード」にするのが推奨です。
でも、あまり使われているように思いません。
#デフォルトでそのモードなのだけど、「解除方法」が書かれた BLOG が山ほどある。
みんなこのモード嫌がっているみたい。
ところで、PHP は WEB サーバー用として、Javascript は WEB ブラウザ用として、それぞれ人気のある言語です。
セットで勉強中、という人も多いようなのだけど、この二つで変数の「宣言しなかった時の挙動」が正反対。
言語を学ぼうとする人に、思わぬ障害になっているようです。
最後の方、FORTRAN の話からどんどん離れていきました。
まぁ、FORTRAN から始まる「変数の話」ということで。
同じテーマの日記(最近の一覧)
関連ページ
ジョン・バッカスの命日(2007)【日記 15/03/17】
ジョン・バッカスの命日(2007)【日記 15/03/17】
ハーマン・ホレリス 命日(1929)【日記 15/11/17】
ジョン・バッカスの誕生日(1924)【日記 14/12/03】
別年同日の日記
申し訳ありませんが、現在意見投稿をできない状態にしています。 |