gosh> (define (>> f g) (.$ g f))
gosh> (define (sq x) (* x x))
gosh> (define (inc x) (+ x 1))
gosh> (define f (>> inc sq))
gosh> (f 3)
16
Prelude> let (>>) f g = g . f
Prelude> let sq x = x*x
Prelude> let inc x = x+1
Prelude> let f = inc >> sq
Prelude> f 3
16
Prelude> let g = inc >> sq >> inc
Prelude> g 3
17
gosh> (define g (>> (>> inc sq) inc))
gosh> (g 3)
17
いや、これはあかんでしょ.
gosh> (define-syntax >>
(syntax-rules ()
((_ f g) (.$ g f))
((_ f g h ...) (>> (.$ g f) h ...)) ))
gosh> (define g (>> inc sq inc))
gosh> (g 3)
17
それより、ついつい
gosh> (define-syntax >>
(syntax-rules ()
((_ f g) (.$ g f))
((_ f g ...) (>> (.$ g f) ...)) ))
と書いてしまう.まだまだ慣れが足りない. しかし、それでも何かしら定義は出来て、そうすると
gosh> (define g (>> inc sq inc))
gosh> (g 3)
18
となった.これは一体なんだ? << wakaran
Prelude> let (|>) x f = f x
Prelude> 2 |> inc |> sq |> sq
81
gosh> (define-syntax \>
(syntax-rules ()
((_ x f) (f x))
((_ x f g ...) (\> (f x) g ...)) ))
gosh> (\> 2 inc sq sq)
81
Schemeで |
は使えない.へんちくりんな変数名を |
で括るから.
gosh> (define |"| 1)
|"|
gosh> |"|
1
Haskellみたいなの方が、コードが少なくなるのは知ってるんだよ. まあ、どうせ趣味で使うんだから、その時々で好きなの使えばいいんだ.
もしかしてと思ってできたけど、.$は別に引数2つに限ってなかった. そうか、LISPのLISPたる所以は、引数はたいていいくつでもいいんだ. listをapplyしてるのが正しい形なんだから.
つまり、+という演算子は、Haskell含めてたいていは二項演算子だけど、 LISPだと、 (+ 1 2 3 ...)
というのが当たり前
gosh> (define h (.$ sq sq inc))
gosh> (h 2)
81
gosh> (define (>> . ls) (apply .$ (reverse ls)))
gosh> (define i (>> sq sq inc))
gosh> (i 2)
17
gosh> (define (\> x . ls) ((apply >> ls) x))
|\\>|
gosh> (\> 3 sq inc)
10
この \>
は、>>
が関数だからこそ定義できた.
define-syntaxは長くなるから嫌だな.define-macroってのもあるみたい だけど、引数の数でmatchさせるようには出来てないみたいだし、 正直に言えば、LISPのマクロはなにがそんなにすごいのか分かってないです. 引数の評価を遅らせる版の関数で、あとcase-lambdaみたいなことができる、 くらいにしか.
そうそう、学校の課題でGCを実装した.調べたりで構想ができるまでに 2日くらいかかったけれど、実際のコーディングは5時間程度で、デバグ まで出来た. 「世界初の記念すべきGCアルゴリズムはマークスイープGC[1]です. はじめて世に出てから半世紀近くが経過した今も、様々な処理系で 用いられている偉大なアルゴリズムです.」---(「ガベージコレクション のアルゴリズムと実装」中村成洋/相川光 共著 より)
長さ2000くらいの配列を延々と確保させてそれをゴミにするような プログラムを動かす.初めはヒープの上に配列を上から作っていって、 残りが2000未満になったら、mark and sweep を行う.残り、というのは ヒープの限界 - ヒープレジスタ値 で計算する.mark sweepはヒープ上のゴミオブジェクトをゼロで塗りつぶす 作業.本当はフリーリストとして持っておくんだけど.でもこの作業の 後、ヒープレジスタ値は変わらない.だって、そうでしょう
heap
0 --+---------+
| object |
+---------+
| garbage |
+---------+
| object |
+---------+
| object |
reg_h-+---------+
| |
| empty |
: :
limit +---------+
reg_hの値は、heapの上からつめていって、(一番上をゼロとして、アドレスが 増える方向を下としてる)、空なスペースの一番上のアドレスとしている.
mark and sweep をすると、先のヒープはこうなる.
heap
0 --+---------+
| object |
+---------+
| empty |
+---------+
| object |
+---------+
| object |
reg_h-+---------+
| |
| empty |
: :
limit +---------+
ほら、間に空白が空いただけで、reg_hは変わらない. だから、先の計算では、ヒープの残り容量は変わらないことになる. 従って、先ほど確保しようとしてGCを起こさせた配列確保を再び しようとすると、またGCを起こす.またmark and sweep する.地味に 時間がかかる.まるで使い物にならない.
改良方法はいくらでも思いついたが、残念ながらヤル気が起きない. それよりも、コンパイラ自体を書きなおしたい.少なくともOCamlとかいう 言語以外で.
node.js は、というか V8 は、いつになったら、 javascript 1.7やそれより上に対応するんだろうな.
三年前のフォーラム記事.V8はJavaScriptじゃなくてECMAScriptに従う、と. ECMAScriptなんて、誰も知らないよ.
ちょっと書き捨てるのには、SpiderMonkey を使うにしても、 node.js は捨てれないしな.
function tarai(x,y,z){
return x<=y? y
: tarai(tarai(x-1,y,z), tarai(y-1,z,x), tarai(z-1,x,y))
}
print(tarai(13,6,0));
こんなものを、node.jsとSpiderMonkeyで実行してtimeを比較する. もちろんnode.jsではprintの代わりにconsole.logを用いる.
nodeがnode.jsであり、jsはSpiderMonkey.
cympfh@sazanami:~/test$ node --version
v0.6.12
cympfh@sazanami:~/test$ js -h
JavaScript-C 1.8.5 2011-03-31
usage: js [options] [scriptfile] [scriptarg...]
Options:
-h Display this information
(略)
cympfh@sazanami:~/test$ time node tarai.js
13
real 0m1.008s
user 0m0.980s
sys 0m0.020s
cympfh@sazanami:~/test$ time node tarai.js
13
real 0m0.993s
user 0m0.972s
sys 0m0.012s
cympfh@sazanami:~/test$ time node tarai.js
13
real 0m1.028s
user 0m1.012s
sys 0m0.012s
cympfh@sazanami:~/test$ time js tarai.js
13
real 0m9.148s
user 0m9.137s
sys 0m0.004s
cympfh@sazanami:~/test$ time js tarai.js
13
real 0m9.099s
user 0m9.085s
sys 0m0.004s
cympfh@sazanami:~/test$ time js tarai.js
13
real 0m9.209s
user 0m9.197s
sys 0m0.004s
ちょっと、違いすぎてビビった. これは、やっぱり、SpiderMonkeyは使いたくなくなる.
で、違う言語を持ち出しちゃうけど、
cympfh@sazanami:~/test$ time gosh test.scm
13
real 0m4.423s
user 0m4.056s
sys 0m0.020s
やっぱり、node.js使うことにしよう.