次世代のリリカルLispを作るのはあなたです

8月 22nd, 2010

先日、COMFRK vol. 1を買いに来た方が
「リリカルLispは拡張したりしないんですか」
と質問されたそうです。

私にはもうリリカルLispのソースを触る元気は残っていません。
githubにソースを置いておいたので、自由に改造して下さい。

http://github.com/zick/Magical-Language-Lyrical-Lisp

流行に乗ってgitとか使ってみたけど、さっぱりわかない。
commitとpushってどう違うんだ。

[PrologでSchemeの操作的意味論を実装]Errata

8月 15th, 2010

昨日、無事完売しましたCOMFRK vol. 1の私の記事で
誤字を見つけたので書いておきます。
他に誤字を見つけた方がいましたら、コメントを残してもらえると助かります。

(5ページ目 右側 [おわりに])
- 誤: x=a+b
- 正: x=a-b

[宣伝]COMFRK vol. 1

8月 14th, 2010

今更ながら、宣伝。

名前: COMFRK
日時: 8/14(土) 二日目
場所: 東ア47a
内容: 雑誌1部 COMFRK vol. 1

1. 夏休み子供λ相談室 by ranha
2. Haskellコミュニティ探訪 – 処理系とライブラリを中心にして – by shelarcy
3. 差分のアルゴリズム by cubicdaiya
4. メインメモリアクセスマニュアル by nish
5. C++0xの空、Variadic Templatesの夏 by lyrical logical
6. PrologでSchemeの操作的意味論を実装 by zick
7. ゲームオーバーのすゝめ by mascalade
8. ???

(埒を明ける日々)

という訳で
「PrologでSchemeの操作的意味論を実装」
という記事を書かせていただきました。
本来ははてなようせいとまなぶ Schemeの形式的意味論に載せるつもりだった、
Prologのソースコードの解説です。
大体こんな感じの内容です。

式Mのxをyに置き換える

7月 17th, 2010

λ計算だとか、数学よりの話をするときはよく、

式Mのxをyに置き換えたものを
M[x:=y]
で表す.

なんて定義が出てきます。
本によっては、まったく同じことを表すのに、
[y/x]M
だとか、
M[y/x]
といった記法も使います。

本日、このM, x, yの順番に、意味がある(かもしれない)ことに唐突に気づきました。

Mxyに置き換える
M[x:=y]

M[x:=y]は日本語表記と完全に一致するじゃないですか、
なんか読み易いと思ったらそういうことか。

それから、[y/x]Mは英語だと意味をなします。

Substitute y for all x in M.
[y/x]M

完全に一致。

しかし、そうなるとM[y/x]が浮いてしまうんですが、
これには何か意味があるんでしょうか。
謎です。

#+のちょっといい話

7月 13th, 2010

Lispでは式1個だけをコメントアウトしたいことがよくあります。

(list A B C)

このようなプログラムで、Bをコメントアウトするときに、
行コメントをつかうと非常に不格好になります。

(list A ;B
  C)

ブロックコメントコメント #| … |# を使えばもう少し奇麗に書けますが、面倒です。

(list A #|B|# C)

Schemeの場合、R6RSで式コメントが書けるようになりました。

(list A #;B C)

非常に簡潔です。素晴らしい。

Common Lispで同じことをする場合、#+を使います。
#+はCで言うところの#ifdefであり、環境に依存するコードを書くときによく使います。

(defun do-something-quickly()
  #+allegro ACL専用の処理
  #+sbcl SBCL専用の処理
  #+clisp CLISP専用の処理
  #-(and allegro sbcl clisp) (error "Use ACL, SBCL or CLISP.")
)

#+X Yと書くと、(member X *features*)が真であれば、Yがリードされ、
偽であればYは読み飛ばされます。
*feature*に含まれないシンボルXをわざと書けば、式コメントが実現できます。

;; 例1
(list A #+nil B C)
;; 例2
(list A #+ignore B C)
;; 例3
(list A #+comment B C)

多くの場合、上の3つの例はうまく動いてくれますが、
万が一、*feature*にnilやignoreやcommentが含まれている場合、Bがコメントアウトされません。

どうしたものかと思いつつ、ずっと #+nil を使い続けていたんですが、
どんな環境でも確実にうまくいく方法を見つけました。

(list A #+(or)B C)

#+(or)です。
#+(or X Y …) Zと書くと、
(or (member X *features*) (member Y *features*) …)
が真のときのみ、Zがリードされます。
つまり、orに引数を与えなければZは常にリードされません。
これで、*features*にnilが含まれていないか心配で眠れない夜ともおさらばです。

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

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を中で使ったらまずいとか、色々あるけど、
まあ、一発ネタなんで、深いことは考えないことにします。)

マンガで分かるLisp [Section 3.4]

5月 29th, 2010

またも前のエントリから1ヶ月が経過してました。

時間経つの早すぎ。

マンガで分かるLisp [その12]

4月 24th, 2010

みなさん、覚えていますか?

マンガで分かるLispは「Lispで世界を救う話」だったということを。

マンガで分かるLisp [Section 3.3]

4月 17th, 2010

気がついたら、1ヶ月以上更新が止まってました。

そして、気がついたら大学院生になってました。