AM-Scheme(4)

昨日は、shibuya.lisp#4 へ行ってきた。近山先生のUtiLispの話、竹内先生のVax-11改造の話 面白かったよ。ICの上にICを乗っける、小亀方式って、だれでも考えつくよね。おいらも、昔 よくやったものです。

先生は、NANDゲートの事をおっぱいゲートと言ってらっしゃったけど、NANDよりもNORの方が 張りがあって、おいらはこっちの方が好きです。いわゆるロケット型おっぱいですね。って、 一体、何を聞いてきたんだろう。でも、先生の著著にサインも頂いた事だし、もう一度読み 直してみるかなあ。

夜の宴会は、若い人の前で、CERNのWeb serverがどうのとか、モザイクがどうのとか、爺の 馬鹿話をしてしまい失礼しました。

Lispが分かれば無敵さん、寿限無さん お世話になりました。

gdbとemacsの連携

近頃(でもないかな)は、emacs上でdebuggerを使う場合、debuggerへのインターフェースが 統一的に扱えるよう、gudという仕組みが搭載されてるようだ。C-h b を使って、gdbのキーバインド を調べてみたよ。

C-x SPC         gud-break
C-x C-a C-b     gud-break
C-x C-a C-d     gud-remove
C-x C-a C-f     gud-finish
C-x C-a TAB     gud-stepi
C-x C-a C-j     gud-jump
C-x C-a C-l     gud-refresh
C-x C-a C-n     gud-next
C-x C-a C-p     gud-print
C-x C-a C-r     gud-cont
C-x C-a C-s     gud-step
C-x C-a C-t     gud-tbreak
C-x C-a C-u     gud-until
C-x C-a C-v     gud-pv
C-x C-a C-w     gud-watch
C-x C-a <       gud-up
C-x C-a >       gud-down

一人でgit

生鮮食料品である、今月号のSDを見ていたら、gitの使い方が出ていた。昔はCVSっつう事 で、ちょっとやった事があるけど、svnをすっ飛ばして今はgitだ。時流に乗り遅れない ようにやってみるか。記事を要約すると

cd work
vi .gitignore
git init
git add .
git commit -a -m "original"
git tag 0.0


vi xx.c
git commit xx.c -m "message"
git tag X.X

git管理したいwork-dirに移動。gitに加えたく無いファイル類(例 *.o等)を列挙した .gitignore を作成。続いて、initで、倉庫を作り、add . で、登録したいファイルを 選定。続いて、commitで、登録。ついでにtagを打っておく。ここまでは、初回のみ 一回だけ行なう。

修正を加えたら、適当な所で、commitし、ある程度まとまったら、tagを更新していく。 この繰返しを続ける。

Real World Haskellの現場からAM-Schemeに移植

献本頂いたRWHを最初から丁寧に読んでいる。そしたら、ghcには、唯一の定数として piがあるそうな。他によく使う定数として e があるけど、ghc にはあらかじめ定義して ないとの事。現場で作るらしい。

Prelude> pi
3.141592653589793
Prelude> e

<interactive>:1:0: Not in scope: `e'
Prelude> let e = exp 1
Prelude> e
2.718281828459045

勿論、これはHaskellの考え方であって、利のきいたschemeでは、最初から、両定数は 定義されてる事が多い。今やってるAM-Schemeはどうかというと、軽量を旨としてるので 勿論そんなサービスは無い。無かったら付け足すだけ。取り敢えず、tools.scmにでも 潜りこませておけばいいだろう。潜りこませた結果は、

commit 980dbba57b988bf0be3e8dc0dde5084e31e77197
Author: Sakae Kobayashi <sakae@fb.kuma.net>
Date:   Fri Nov 6 16:45:31 2009 +0900

    Add constant pi,e

diff --git a/tools.scm b/tools.scm
index 45212bd..b5aa0d8 100644
--- a/tools.scm
+++ b/tools.scm
@@ -472,3 +472,6 @@
           (ed-list lis level))))
     (ed-list lis 0)))

+;;; some constant
+(define pi 3.141592653589793)
+(define e (exp 1))

こんな感じになった。 実行してみると、

> pi
3.14159
> e
2.71828

あれ? 折角、精度よく設定したのに、下5桁しか表示してくれないね。勿体ない事です。

> 1234567890.123456
1.23457e+09

大きな数字は、E表現にしてくれちゃうのね。多分、浮動小数点な数は、自動表現になってると思うので、探して書き換えちゃえ。

[sakae@fb ~/v110/src]$ git diff
diff --git a/print.c b/print.c
index 8b65045..4dd3914 100644
--- a/print.c
+++ b/print.c
@@ -68,7 +68,7 @@ int pr;
     } else if (isinteger(l)) {
         length = printinteger(l, file, pr);
     } else if (isreal(l)) {
-        sprintf(&strbuff[0], "%g", rvalue(l));
+        sprintf(&strbuff[0], "%-20.6f", rvalue(l));
         length = strlen(&strbuff[0]);
         if (pr) PrintString(file, &strbuff[0]);
     } else if (isstring(l)) {

後は、commitしておこう。commitしちゃうと、見えなくなっちゃうんだよなあ。

AM-Schemeに手続きを追加しる

さてはて、折角ここまできたので、AM-Schemeに手続きを追加して、俺様Schemeに してみよう。で、10秒考えて、timeを実現するためのプリミティブを追加する。

ANSIでは、廃止の方向で動いている(代わりに、clock_gettime(2) が推奨されているとか) gettimeofday() を使って、EPOC を、マイクロ秒 まで求めてみる。 手続き名は、安直に、gtod とでもしよう。

diff --git a/init.c b/init.c
index f7900f3..2927be9 100644
--- a/init.c
+++ b/init.c
@@ -286,6 +286,7 @@ init_globals()
     mk_prim2(PR_UNBOX, "unbox", MUST_BE_N(1));
     mk_prim2(PR_SETBOX, "set-box!", MUST_BE_N(2));
     mk_prim2(PR_EXIT, "exit", MUST_BE_N(0));
+    mk_prim2(PR_GTOD, "gtod", MUST_BE_N(0)); // gettimeofday

     /* intialization of global pointers to special symbols */
     LAMBDA = mk_sym_from_cstr("lambda", STDCHARCASE);
diff --git a/prim.c b/prim.c
index 3daed4c..3fed7cf 100644
--- a/prim.c
+++ b/prim.c
@@ -10,6 +10,8 @@
  */

 #include "amscm.h"
+#include <time.h>      // For gettimeofday
+#include <sys/time.h>  // dito

 extern int UpperCase();
 extern int LowerCase();
@@ -1171,3 +1173,12 @@ pointer PR_EXIT() /* exit */             /*  Added by Akira Kida */
 {
     longjmp(reset_jmp, 2);
 }
+
+// Add by sakae gtod -- gettimeofday
+pointer PR_GTOD()
+{
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return mk_real(tv.tv_sec + (double)tv.tv_usec*1e-6);
+}
+
diff --git a/prim.h b/prim.h
index 1eae4ed..85a4aa2 100644
--- a/prim.h
+++ b/prim.h
@@ -151,3 +151,4 @@ extern pointer PR_BOXP();
 extern pointer PR_UNBOX();
 extern pointer PR_SETBOX();
 extern pointer PR_EXIT();
+extern pointer PR_GTOD();

init.c に手続き名を登録し、同様にprim.h にも登録。prim.c の頭(でいいのか?)に gettimeofday を使う時の ヘッダーファイルを指定。ファイルの最後に、実体を追加した。

> (gtod)
1257504944.097936
> (let ((st (gtod)))
    (load "tools.scm")
    (- (gtod) st))
;loading tools.scm
 :
pi
e
0.004829

gtodをそのまま使うと、EPOC秒を返す。(let .. (xxx) ...) のようにすると、(xxx)の 実行時間(上記では、load手続き)を、計測できる。結果は、4.8ms となった。

マクロでも、、

gtodで、時間が取れるようになったので、S式の実行時間計測用のコードを書いてみよう。 (time hoge) で、hogeの実行時間を計るようにするわけだが、hogeを評価してからという訳には いかない。こういう時はマクロの出番だ。上記の(let ...)のようにして、hogeを挟みこむ。

> (macro time (lambda (body)
  `(let  ((st (gtod)))
      ,body
      (- (gtod) st))))
time
> (time (fib 10))
Error: Unbound variable f
[return to toplevel]

上記のマクロで良いはずなんだけど、何故か動かない。展開がうまくいかないのかな? bodyを定義しておいてから、手動で展開して、それを実行してみる。

> (define body '(fib 10))
body
> `(let  ((st (gtod)))
      ,body
      (- (gtod) st))
(let ((st (gtod))) (fib 10) (- (gtod) st))
> (let ((st (gtod))) (fib 10) (- (gtod) st))
0.002304

これでは、うまくいっているな。何故だめなんだろう? 後で潜ってみるか。