2008-01-07
■ [biwascheme] write_ssを実装した
相互再帰関数の実行をデバッグしようとしたら、循環構造を文字列化しようとして無限ループになることが分かったので、 write/ssのようなものを実装した。
gosh> (let ((x (list 'a))) (list x x)) (#0=(a) #0#)
こういうやつ。「#n=」で名前を付け、「#n#」で参照する感じ。
どうやればいいか分からなかったので、とりあえず2パスで実装することにした。
- まずダンプしたいオブジェクトを再帰的に辿り、複数回出現するオブジェクトを全てメモする。
- このメモを見ながら、オブジェクトを再帰的に文字列化していく。
- 2度以上出現するオブジェクトで、初回の出現なら、#n= を頭に付ける。
- 2度以上出現するオブジェクトで、2回目以降の出現なら、単に #n# と出力する。
とりあえずletrec*を使ったコードのデバッグに使ってみたら、こんなんが出てきて吹いたw
#0=[[#1="frame", [#1#, [#2="constant", #3=1, [#4="argument", [#5="refer-local", #6=0, [#7="indirect", [#4#, [#8="refer-global", #9="-", [#10="apply", #11=2]]]]]]], [#4#, [#12="refer-free", #6#, [#7#, [#10#, #3#]]]]], [#4#, [#2#, #3#, [#4#, [#8#, #13="+", [#14="shift", #11#, #3#, [#10#, #11#]]]]]]], [[[#1#, [#5#, #6#, [#7#, [#4#, [#8#, "zero?", [#10#, #3#]]]]], ["test", [#2#, #6#, ["return", #3#]], [#1#, [#1#, [#2#, #3#, [#4#, [#5#, #6#, [#7#, [#4#, [#8#, #9#, [#10#, #11#]]]]]]], [#4#, [#12#, #3#, [#7#, [#10#, #3#]]]]], [#4#, [#2#, #3#, [#4#, [#8#, #13#, [#14#, #11#, #3#, [#10#, #11#]]]]]]]]], [#15=#<undef>], [#0#]]], [#15#]]
あー、数値はimmutableだからタグ付ける意味ないのか。 (readしたときに同型の構造ができる、ってのがこの表記の目的らしいので。) 文字列もJavaScriptレベルではimmutableなんだから タグ付けるのやめようかなぁ。
Gaucheの場合
write.cを見てみたけど、Gaucheも2 passなんだな。
/* We need two passes to realize write/ss. The first pass ("walk" pass) traverses the data and finds out all shared substructures and/or cyclic references. It builds a hash table of objects that need special treatment. The second pass ("output" pass) writes out the data.
ただ、発見したオブジェクトのテーブルは配列じゃなくハッシュテーブルを使ってるらしい。 そりゃそうか。