oreore_lisp(1)

先日から、cdr 作業、正確には、CD-R 作業をしている。CD+R でも、CD-RWでもCD-RAMでもCD+RW でも良かったんだけど、一番コストパフォーマンスが良さそうな、CD-Rである。

そろそろヤバくなりつつあるWindows機をバックアップしておこうと思って、DVD Writer を買ってきたのだ。DVDの事なんて良く知らないおいらは、山田君に相談したよ。そしたら彼は BuffaloのDVSM-X20U2Vを勧めてくれた。6980円也。

生DVDは、日本製がいいよとの事だったので、Made in Japan なSONY製にした。(特価だったので) タイマー発動は 無いだろうな。まあ、DVD(にしろCDでも)は、何処のやつを使っても、いずれ発動しちゃう から、気にしてもしょうがないか。

買ってきたWriterは、X20倍速対応のUSB接続のやつだ。付属のユーティリティを使うとX20で 使えるようになるらしいけど、面倒なので止めた。ドライブ自身は、Optiarc DVD RW AD-7240Sと 名乗ってたよ。

実際にコピーを始めると、結構な率で、Write fail で失敗する事がある。ドライブとメディアの 相性が有るって話を聞いてはいたけど、実際に経験してみると腹だたしい。 Buffaloでは、推奨メディアとして、(CD-Rの場合)太陽誘電、マクセル、三菱、SONYを上げて いたけど、自分で調べてみる鹿。

CD-R実験室Optiarc AD-7240S というのが出ていた。よくもまあ、いろいろと調べてますなあ。感心するやら呆れるやら。 と、このページからドライブのメーカーへ飛んで行くとSONY製だった。SONYと仲の良いメディア メーカーって何処だろう? 中立性を取って、太陽誘電かなあ。

DVD Writerなんかをチョイスするより、割り切って、外付けHDDにでもしておいた方が良かった かしらん。

Lispの解説見つけたよ

吉田さんが書かれた、ちょっと変わったLisp入門を見つけたので、忘れないうちにメモ。 これって、今やってる oreore_lisp の解説記事でもあるんだ。

興味深げに読ませていただきました。読んだ後は、手を動かそうとソースを見始めた んだけど、ちとおかしな所を発見。附属してる、base.lispを読み込む部分だ。ちょっと 見直しついでに、oreore に改造してみたよ。

[sakae@fb ~/oreore_lisp]$ diff -u oreore_lisp.c.org oreore_lisp.c
--- oreore_lisp.c.org   2009-11-11 12:42:14.000000000 +0900
+++ oreore_lisp.c       2009-11-11 12:44:36.000000000 +0900
@@ -22,7 +22,7 @@
 #define UNDEF           ((cell)2)

 #define SPECIAL_TOKENS  "().'"
-#define LOAD_FILE_ESCAPE "\\f "
+#define LOAD_FILE_ESCAPE ":l "
 #define DEFAULT_FILE_EXT ".lisp"

 #define IS_INT(n)       (((int)n & 0x00000001) != 0)
@@ -150,7 +150,7 @@
       if (input_stream == NULL) {
        if (get_one_line("lisp> ")) exit(1);

-       if (strncmp(read_buffer, LOAD_FILE_ESCAPE"\\f ", strlen(LOAD_FILE_ESCAPE)) == 0) {
+       if (strncmp(read_buffer, LOAD_FILE_ESCAPE, strlen(LOAD_FILE_ESCAPE)) == 0) {
          open_load_file(strtok(&read_buffer[strlen(LOAD_FILE_ESCAPE)], "\r\n"));
          continue;
        }

typoが残っていたのと、ファイルを指定する接頭語を、acl っぽくしたのだ。この結果 lispのプロンプトが出てる所で、次のようにして、ファイルをロード出来る。

lisp> :l base
def
defmacro
if
%
null
apply
mapcar
lisp>

嗚呼、ついでに、defun も、ちと近代風にしちゃった。defって、rubyでもpythonでも お馴染みだもの!

on gdb

また、懲りもせずgdbを使って動きを追ってみたいんだけど、ソース上では、CAR(e)のように マクロが多用されていて、gdbのpコマンドを使う時ちと不便だ。みなさんは、こういう時どうしてる んですかね?

私は、馬鹿の一つ覚えで、.gdbinitに次のように定義した。

define car
   print ((cons_t *)$arg0)->car_e
end

define cdr
   print ((cons_t *)$arg0)->cdr_e
end

define atom
   print ((symbol_t *)$arg0)->name
end

最後の atomと言うのは、Lisp 1.5風に言うと、印字名を表示する意図なので pname の方が 適切かと思ったけど、何となく、atomってのを使ってみたかったので。。

lisp> (cons 123 ())

Breakpoint 1, eval (e=0x804e328, env=0x0) at oreore_lisp.c:548
548       if (e == NIL || IS_INT(e)) return e;
(gdb) n
549       if (IS_SYMBOL(e)) {
(gdb)
556       return apply(CAR(e), CDR(e), env);
(gdb) p e
$2 = 0x804e328
(gdb) car e
$3 = 0x8088a80
(gdb) cdr e
$4 = 0x804e330
(gdb) n

Breakpoint 1, eval (e=0x8088a80, env=0x0) at oreore_lisp.c:548
548       if (e == NIL || IS_INT(e)) return e;
(gdb) atom e
$5 = "cons", '\0' <repeats 27 times>

もう少し、便利なgdbコマンドを増やしておいた方がいいな。

oreore改造

前回やった、時間計測マクロに再び挑戦してみる。まずはプリミティブな手続きの追加だな。 このLispは設計上の都合から、32Bitのintは扱えない。cellにintの即値を格納する都合上 intとポインターを区別出来るようにしてるからだ。

具体的には、格納したいintを1ビット左にシフトし、LSBに1をセットしてる。 (この結果、CELL値で奇数になってるのは、intと判定出来る)すなわち、31Bitのintが 扱えるのだ。

困った事に、EPOC秒も折り返し点を過ぎているので、そのまま格納すると、結果がオーバーフローして マイナス表示になる。だったら、格納する前に適当な値を引いてしまえと。

どんな値を引いてもいいんだけど、区切りよく、21世紀の始まりをEPOCの0秒としよう。 えっと、2001年1月1日が21世紀の始まりだったよな。EPOC秒にすると、

sakae@nil ~/oreore_lisp]$ irb
irb(main):001:0> Time.local(2001).to_i
=> 978274800
[sakae@nil ~/oreore_lisp]$ date -r 978274800
Mon Jan  1 00:00:00 JST 2001

この数値を引いて、oreore時間にしちゃえ。

cell gtod() {
  struct timeval tv;
  int            s,u;
  gettimeofday(&tv, NULL);
  s = tv.tv_sec - 978274800;   // diff from 2001/01/01
  u = tv.tv_usec;
  return cons(INT2CELL(s), INT2CELL(u));
}

嗚呼、init()の所へ、この関数を登録を忘れないようにしなければ。で、試運転。

isp> (gtod)
(279738516.695681)
lisp> (gtod)
(279738544.139247)

何となく、浮動小数点表示してるのは、Lispのマジックっつう事ですんで、間違い無きように。

時間の引き算

上記で、まがいものの時間情報を得られるようになったので、それ用の時間差を計算する コードを書いてみる。実験台は、fib に決まってるので、実験出来るようについでに準備を整えておく。

(def < (a b)
   (cond
     ((> a b) nil)
     ((= a b) nil)
     (t       t)))

(def fib(n)
  (if (< n 2)
     n
     (+ (fib (- n 1)) (fib (- n 2)))))

(def diff(a b)
   (cond
     ((> (cdr a) (cdr b))
          (cons (- (car a) (car b))
                (- (cdr a) (cdr b))))
     ((= (cdr a) (cdr b))
          (cons (- (car a) (car b))
                 0))
     (t
          (cons (- (- (car a) (car b)) 1)
                (- (+ (cdr a) 1000000) (cdr b))))))

少なりの < は、> の論理を反転させる if を書けばいいかと思ってたけど、ちゃんと場合分け しないと、おかしな事になるね。危ない、危ない。 そんじゃ、ちょっと、diffの試験をば。

lisp> (setq old (gtod))
old
lisp> (setq new (gtod))
new
lisp> new
(279745591.203011)
lisp> old
(279745577.178021)
lisp> (diff new old)
(14.24990)

ははは、紛い物ゆえにボロが出ましたなあ。小数点以下の表示が6桁に満たない場合、頭の中で 小数点のすぐ右隣に"0"を補ってください。(うん、我ながら酷い仕様だ事。printルーチンを いじって、%06d しちゃうかな。微妙だ。) そんじゃ、気を取り直して、もう一丁。

lisp> new
(279746091.355922)
lisp> old
(279746081.949215)
lisp> (diff new old)
(9.406707)
lisp> [sakae@nil ~/oreore_lisp]$ bc
279746091.355922 - 279746081.949215
9.406707

どうやら、借りのある計算もOKのようです。

マクロへの下実験

道具立ては揃ったようなので、下実験してみます。

lisp> (def test ()
lisp>   (setq old (gtod))
lisp>      (fib 15)
lisp>   (diff (gtod) old))
** ERROR : Wrong number of arguments 'def' requires 3, but 5

はて、どうしますかね。begin or progn or let ? 何がいいのだろう。