SchemeでBrainFuck その2
前回書いたBrainFuckインタプリタはかなりおそかった。与えられたbfコードをリストとして構築しなおしてから、さあ実行、とかやってたからだろうか。リストの扱いくらいしかわからなかったからそうなったのだが。
bfのコードはそのままstringとして扱った方が簡単でした。あと速かった。
http://www.shiro.dreamhost.com/scheme/wiliki/wiliki.cgi?Scheme%3ABrainfuck
で拾ってきたquineのコードによるベンチマーク。前回書いたやつと、今回書き換えたやつにて。コード内部に直接quineのコードをぶちこんで、bf-run。
% time gosh bfi-ls.scm >/dev/null real 0m5.240s user 0m5.220s sys 0m0.030s % time gosh bfi-str.scm >/dev/null real 0m0.170s user 0m0.140s sys 0m0.030s
30倍の高速化に成功! とか言ったらかっこいいだろうか。よくはないな。元々の30倍も高速化できるほどの駄インタプリタ書いたの俺だし。
コマンドライン引数与えたbfファイルを処理できるようにしてみた。Gaucheで動くことは確認。
% gosh bfi.scm hello.b Hello World! % cat hello.b >+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++.
以下ソース
(define (bf-run code) (define (bf-inc! mem mp) (vector-set! mem mp (+ (vector-ref mem mp) 1)) mem) (define (bf-dec! mem mp) (vector-set! mem mp (- (vector-ref mem mp) 1)) mem) (define (bf-put mem mp) (write-char (integer->char (vector-ref mem mp))) mem) (define (bf-get! mem mp) (vector-set! mem mp (char->integer (read-char))) mem) (define (bf-mem? mem mp) (if (= (vector-ref mem mp) 0) #f #t)) (define (bf-loop code cp mem mp) (let ((ret (iter code (+ cp 1) mem mp))) (if (< (car ret) (string-length code)) (iter code cp mem (cdr ret)) ret))) (define (bf-loopout code cp) (define (iter code cp nest) (if (= nest 0) cp (let ((c (string-ref code cp))) (cond ((eq? c #\[) (iter code (+ cp 1) (+ nest 1))) ((eq? c #\]) (iter code (+ cp 1) (- nest 1))) (else (iter code (+ cp 1) nest)))))) (iter code (+ cp 1) 1)) (define (iter code cp mem mp) (if (< cp (string-length code)) (let ((c (string-ref code cp))) (cond ((eq? c #\+) (iter code (+ cp 1) (bf-inc! mem mp) mp)) ((eq? c #\-) (iter code (+ cp 1) (bf-dec! mem mp) mp)) ((eq? c #\>) (iter code (+ cp 1) mem (+ mp 1))) ((eq? c #\<) (iter code (+ cp 1) mem (- mp 1))) ((eq? c #\.) (iter code (+ cp 1) (bf-put mem mp) mp)) ((eq? c #\,) (iter code (+ cp 1) (bf-get! mem mp) mp)) ((eq? c #\[) (if (bf-mem? mem mp) (bf-loop code cp mem mp) (iter code (bf-loopout code cp) mem mp))) ((eq? c #\]) (cons cp mp)) (else (iter code (+ cp 1) mem mp)))) (cons cp mp))) (iter code 0 (make-vector 30000 0) 0)) (define (bf-run-file file) (define (iter port) (let ((c (read-char port))) (if (eof-object? c) () (cons c (iter port))))) (bf-run (list->string (iter (open-input-file file))))) (define (main args) (if (null? (cdr args)) (display "bfi bffile\n") (bf-run-file (cadr args))))