Archive for 6月, 2010

##のわりとどうでもいい話

火曜日, 6月 8th, 2010

※リストの循環は脳に悪影響を及ぼす可能性があります。
 本エントリを読むときは必ず(setq *print-circle* t)を利用して下さい。

<括弧を書かずに循環構造をつくろうとしたのがことの始まりでした>

'#1='#1# => #1='#1#

リーダマクロ ‘ を展開すると結果は、 #1=(quote #1#) になり、循環構造ができます。
しかし、CLISPでこれのcdrを取るとスタックオーバフローします。

(cdr '#1='#1#)
*** - Program stack overflow. RESET

ちなみに、SBCLだとちゃんと結果が表示されます。

(cdr '#1='#1#) => (#1='#1#)

<ちゃんと!?>
よくよく見ると、微妙におかしなことに気づきました。
#1=(quote #1#)は2個のコンスセルから構成されます。
しかし、(#1=’#1#)は3個のコンスセルから構成されてるじゃないですか!
(cdr ‘#1=’#1#)は#1=((quote . #1#))じゃないとおかしいはず(図参照)。

<何かがおかしい>
もし(#1=’#1#)が図の右側のような構造で表されていた場合、
(#1=’#1#)と(cdar (#1=’#1#))は別のものになるはずです。しかし、

(defvar a (cdr '#1='#1#)) => A
a => (#1='#1#)
(eq a (cdar a)) => T

同一のものと判定されてしまいました。
しかし、

(defvar b '(#1='#1#)) => B
(eq b (cdar b)) => NIL

やっぱり同一じゃなかった!?
つまり、ここから得られる結論は
(#1=’#1#)は(#1=’#1#)であって(#1=’#1#)ではない、なんじゃそりゃ。

<更なる謎>
CLISPで色々試していると更に訳の分からないことが起きました。

(cdr '#1=(quote #1#))
*** - Program stack overflow. RESET
(cdr '#1=(q #1#)) => #1=((Q . #1#))

quoteだと駄目で、qなら大丈夫!?
シンボルが別のものになっただけで、なんで結果が変わるのでしょうか。

<それでもGaucheなら・・・Gaucheならきっと何とかしてくれる>
もうCommon Lispの処理系に頼るのに嫌気がさして来たのでGaucheを使ってみました。

'#1='#1# => #0='#0#

Gaucheだと##は0から始まるみたいです。で、これのcdrをとると

(cdr '#1='#1#) =>
#0=('''''''''''''''''''''''''''''''''''''''''''
'''''''''''''''''''''''''''''''''''''''''''
(中略)
'''''''''''''''''''''''''''''''''''''''''''
Segmentation fault

やたらと沢山の引用符が出た後に死んでしまいました。

ここまで来てやっとすべての謎が解けました。
CLISPがスタックオーバフローを起こした原因は(恐らく)この引用符です。
表示部分で(quote X)を’Xに変換する処理が素直であったために死んでしまったのでしょう。

一方、素直でない(?)SBCLは生き残ることに成功しましたが、
(#1=’#1#)という表示と(#1=’#1#)という入力が別物になるという問題が生まれてしまいます。
個人的にはLispWorksの動作が一番好きです。

with-open-fileをC++/C99で

木曜日, 6月 3rd, 2010

Common Lispにはwith-open-fileというマクロがあります。

(with-open-file (stream filename)
  ...
  (read-line s)
 ...
 )

このマクロは、ファイルをオープンして、
ここを抜けるときに自動的にファイルをクローズしてくれるというものです。
そのため、ファイルの閉じ忘れがおこりません。

このマクロをC++、もしくはC99で再現する方法を思いついたのでメモしておきます。

#define with_open_file(s,p,m) \
  for(FILE *s=fopen(p,m); s; fclose(s),s=NULL)
...
void hoge(char *path) {
  char buf[256];
  with_open_file(fp, path, "r") {
    ...
    fgets(buf, sizeof(buf), fp);
    ...
  }
}

短いコードでなかなかいい感じだと思います。
残念ながら、本物のwith-open-fileと異なり、returnなどで関数を抜けたときに、
ファイルを閉じてくれないという問題がありますが。
(その他にも、breakやcontinueを中で使ったらまずいとか、色々あるけど、
まあ、一発ネタなんで、深いことは考えないことにします。)