値を後から取ってくる
現実逃避。
デバッグ目的でインタプリタに直接コードを書いてると、しまった、この値は変数に束縛しておくべきだった、というような事がある。例えば、なんか計算して答えがでたんだけど、その答えの桁が多すぎて概算がよくわからない、というような場合。例えば、(fact 10000)とか打ち込んで、99990000とか答えが返ってきても、それは何桁なのかしら、とか。あと、メモリ使用量がどれぐらいか出してみたけど、バイト単位ではよくわからんのでMB単位に直したいとか。
このぐらいなら、もう一回式を打ち込んでも問題ない。Emacsから使ってたらヒストリからもう一度入力するのはM-p一発だ。だけど、副作用があるような関数を評価した結果として返ってきた値の場合はそういう訳にもいかない事もある。
そういうわけで、あの値が欲しい、となったときに、それがちゃんと束縛してあるよ、という、ドラえもんのようなインタプリタは無いものか、とずっと前から考えていたのだが、そもそもどういう風に値をとってこさせればよいのか、インターフェースを思いつかなかった。
最近Railsの影響でRubyをまたちょっと使うようになって、irbのプロンプト(irb(main):001:0> 見たいな奴)を見て、履歴として値を束縛しておけば良いのか、と思いついたので、ちょっと実装してみた。(irbが値を取ってこれるようになっているのかは知らない。)
プロンプトに何回コマンドを打ったか表示されていて、n回めに打った式の値はhnで取ってこれる。(hはhistory。)1つめの式の値はh00で参照、2つめの式の値はh01で参照、というような感じ。以下はcutで引数に1を足すというクロージャを作って、それをh00という変数で参照する例。
igosh:00 > (cut + <> 1) #igosh:01 > (h00 1) 2
実装にはread-eval-print-loop関数を使った。(selectorでも実装してみたが、C-dで終了を自力で対応しないといけないとか、めんどくさかったのでやめた。)グローバル変数を使ってるのがいやーんな感じだが、これはどうすればいいのかよくわからない。
(define gval #f) (define history-counter 0) (define (bind-result val) (let* ((val-name (format #f "h~2,'0d" history-counter)) (val-sym (string->symbol val-name))) (set! gval val) (eval `(define ,val-sym gval) #f)) (inc! history-counter) val) (define (evaluator exp env) (bind-result (eval exp env))) (define (prompter) (format #t "igosh:~2,'0d > " history-counter) (flush)) (define (main args) (read-eval-print-loop #f evaluator #f prompter))