2014年04月01日の日記です


おかしな言語仕様  2014-04-01 11:47:07  コンピュータ

エイプリルフールなので、と断ったうえで。


プログラム言語は厳密であいまいなところがない、と思われがちだけど、そんなことは無い。

人間が作ったのだから、非常に曖昧で、おかしな規則を含んでいることがある。


そんな例をいくつか書き出してみよう。




C言語と、その影響をうけた数多くの言語(現代のほとんどの言語!)では、10 進数で 0 を書くことができない。


0 なんて、非常に当たり前に使うものだ。そして、C言語は10進数で数字を表記できる。

でも、0 を10進数で書くことはできない。


言語なので、まず語句を識別する。

そして、この語句の先頭が数字で始まっていると、何らかの数値と見なす。


ここで、1-9 の数字で始まっている場合は、10進数と見なされる。


0 から始まっている場合は複雑で、続く文字を見る。

この文字が x なら 16進数。

C言語では使えないが、一部言語では bで2進数も扱える。


これらの規則に合わない場合は、8進数だ。


そう、0 から始まって、他の規則に合わない場合… 単に 0 と書いた場合は、それは10進数の 0 ではなく、8進数の 0 なのだ。



混乱するから数値はすべて 10進数で表記、とか考えている人は要注意。

知らず知らずのうちに、8進数が混ざっている。


そういう言語仕様なのだから仕方がない。

混乱してバグが出ないように祈っておこう。




同じく C 言語では、改行が意味を持たない。

古いタイプの言語…FORTRAN や BASIC では、文の区切りは改行で、非常に重要だ。


C 言語では改行はただのスペースと同じで、区切りは ; を使う。

この考え方はその後の多くの言語(HTML なども)に受け継がれている。



でも、C と一緒に使う「プリプロセッサ」は、改行を認識する。

命令の有効範囲は改行まで、と言うものがほとんどだ。


どうしても長く書きたくて改行が邪魔になるときは、行の最後に \ を入れることになっている。

\ は、その次の文字に特別な意味を与える、という C 言語での表記方法で、行の最後にこれを書くことによって、続く改行の意味を打ち消している。


ところで、C が進化して C++ になった時、改行を活用する命令が追加された。

// で始まる「行コメント」で、このコメントは、改行によって終了する。


じゃぁ、// で始まる行の最後に \ を書くとどうなるのか。

// はコメントなので、その内部の \ を認識してはならない。その一方、\ に続く改行は無視されなくてはならない。


改行は意味を持たないはずの C 言語に、便利だろうと思って改行を区切りとする命令を追加してしまったがゆえに、非常に困ったことになった。

でも、// でコメント、というのは今でも多くの言語に受け継がれている。



大昔、Apple が Mac (68k) 用に発売していた C++ では、マニュアルのかなり最初の方のページで「既知のバグ」として、わざわざこの件に言及していた。

C++ の仕様の穴であり、Apple のあずかり知ったことではない、と言わずにはいられなかったのだろう。


この機能を使ってはならない。何が起こるかはわからない。




どんな言語でも、「0文字の文字列」は表現できると思う。いわゆるヌル文字列だ。

正規表現もまた、文字列で表現される。そして、ヌル正規表現と言うのも存在し得る。


perl だと、ヌル正規表現によって文字列を分割すると、文字を1文字単位でバラバラにできることが知られている。

ヌル正規表現は、活用すると便利なものなのだ。


でも、Javascript では、この便利な正規表現がつかえない。

Javascript では /~/ で正規表現を書くが、 ヌル正規表現のつもりで // とすると C++ 由来の行コメントとなってしまい、以降の命令を無視してしまうのだ。


sed 由来の書き方と C++ 由来の書き方を混ぜてしまったがゆえにこうなった。

混ぜるな危険! である。




同じような問題は SQL でも発生する。


SQL は、大抵は別の言語の中から呼び出され、ユーザーからは見えない部分でデータを保持するのに使用される。


この時、言語によって SQL の命令の中に数値が埋め込まれたりする。


ところで、「ある数の符号を逆にしたい」ことはよくある。

当たり前の話だけど、変数の前に - をつければ、符号は逆になる。


ところが、すでにマイナスの数だと -- となってしまうことがある。そして、これは SQL ではコメントを意味する。


このため、符号を逆にしようと思ったらコメントが発生してエラーになる、と言うことが起こりうる。

注意しなくてはならない。




その昔、FORTRAN の時代にはパンチカードでプログラムを作った。


パンチカードは80文字しか文字を記録できず、文は必ず80文字以内に書かなくてはならない。

何より、パンチカードは高価だった。


そこで、文字を詰め込んで書いても良い言語仕様だった。

スペース区切りなどいらない。単語を連続して書いても、単語として認識できる綴りであれば問題はない。


それどころか、語句の途中にスペースを入れても構わない。スペースは単に無視して処理されるのだ。

(ただし、文字列定数内のスペースは正しく保持される)


DO 100 I=1,10

WRITE (*,*) I

100 CONTINUE


では、上のプログラムと、次のプログラムは同じものか。


DO 100 I=1.10

WRITE (*,*) I

100 CONTINUE


…残念ながら違う。

何が違うかと言うと、1行目の , (カンマ)が . (ピリオド)に変わっている。


でも、文法エラーにはならず、実行できる。


上のプログラムは、1 ~ 10 の数字を印字するものだ。

下のプログラムは 0 と表示して終わってしまう。



DO 100 I=1,10 は、100行目までの間を繰り返す、その際に I を 1 から 10 まで変化させる、と言う命令だ。

でも、DO 100 I=1.10 は、 DO100I という名前の変数に、 1.10 という実数を代入している。


古い FORTRAN では、変数は1文字目によって「型」が決まり、宣言なしに使用できた。

Integer の I から、Number の N まで… I J K L M N で始まる名前は整数、それ以外は実数。

変数名は6文字までの長さで、初期値は 0、または 0.0 だった。


何とも運の悪いことに、DO100I は6文字の名前の変数であり、実数を代入できた。

だから、1.10 を入れてもエラーにはならない。


そして、I を使用しても、単に初期値の 0 が使われるだけで、こちらもエラーにならない。


これは、計算機言語の設計の話になると、必ず出てくる例だ。

FORTRAN は、その言語仕様上、文法ミスの検出が難しかった。



もちろん、今の FORTRAN では言語仕様も拡張されていて、このような書き方は推奨されない。

でも、互換性のために今でもこの書き方はできるらしい。




lisp では、t nil で真偽を示す。

lisp にどっぷりはまった lisp プログラマは、日常会話でも YES / NO の意味で t nil を使うという。


そして、lisp プログラマはコーヒーを飲めない、という状態が発生する。


アメリカでは、何か飲み物、と言えば普通はコーヒーなので、飲み物がいるかと尋ねられるときは、「Would you like a coffee?」だ。


ここで「t」と答えると、相手はなんと紅茶を持ってきてくれてしまう。




このネタ、いくらでも続けられそう (^^;


でも、すぐに思い出せたのはこのくらい。

あとで追記するかも。




同じテーマの日記(最近の一覧)

コンピュータ

関連ページ

変数名の話【日記 14/10/15】

別年同日の日記

04年 呪われているのか?

09年 春眠暁を覚えず

15年 ビッグエンディアンとリトルエンディアン(1980)

15年 エド・ロバーツ 命日(2010)

16年 卒園

21年 異世界転移の方法

23年 城ヶ島


申し訳ありませんが、現在意見投稿をできない状態にしています


戻る
トップページへ

-- share --

8000

-- follow --




- Reverse Link -