Edwin 又は mit-scheme
emacsもどき、色々有ったなと考えていると、mit-schemeにemacsもどきが付属してる事をを思い出した。早速行ってみると、 schemeで書いたものらしい。mit-schemeに組み込まれているようで、分離は出来ないようだ。
これって、昔M$がIEはWindowsと一体になってますから分離出来ないって言ってたのと一緒の 構図だな。
8 Edwin によると、
sakae@uB:~$ mit-scheme --edit
こんな風に起動するか
sakae@uB:~$ mit-scheme MIT/GNU Scheme running under GNU/Linux Type `^C' (control-C) followed by `H' to obtain information about interrupts. Copyright (C) 2011 Massachusetts Institute of Technology This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Image saved on Tuesday October 22, 2013 at 12:31:09 PM Release 9.1.1 || Microcode 15.3 || Runtime 15.7 || SF 4.41 || LIAR/i386 4.118 Edwin 3.116 1 ]=> (edit)
replから呼び出すみたい。例によって、statusライン。それっぽい。
--**-Edwin: *scheme* (REPL: listen)----All----------------------
うぶに入れたんだけど、一式は下記の場所に鎮座してた。
sakae@uB:/usr/lib/i386-linux-gnu/mit-scheme$ ls all.com edwin imail optiondb.scm sf ssp xml cref ffi lib runtime sos star-parser
edwinなんてのが有るんで中を覗いてみたら、哀れな事にバイナリーコードだった。 これはいかんって事で、ソースを取ってきたら、本当にschemeで書かれていた。
edwinの本体と言うかtopは、edwin/editor.scmだ。ここから見て行くとよかろう。
Windowsでもmit-scheme
これを機会にWindowsにも入れてみた。だって、マニュアルが同梱されてると思ったから。 (その期待は見事に裏切られたけどね)
DeskTopに出来るアイコンの中身は下記のようだった。ついでに、作業フォルダーを設定しておくと吉。 それぞれがフルパスで登録してあるって事は、PATHの設定をサボったのかな。その割には、 コンパネのプログラムの所を見ると、しっかり登録されてる。まあ、そういう事もあらーな ぐらいにしておこう。 使うにはEdwinを強制される。
"C:\Program Files\MIT-GNU Scheme\bin\mit-scheme.exe" --library "C:\Program Files\MIT-GNU Scheme\lib" --edit
しょうがないので、サイトにあるマニュアルを見てたら、コンパイル出来るよなんて書いてあった。 schemeでコンパイルなんて珍しいな。使った事はないけど、一つだけ知ってる。 (chez (chez scheme))。インタプリター版は無料だけど、 コンパイラー版は有料。
ソースに付属してたREADMEを見ると
* "sf" contains a program that translates Scheme source code to an internal binary format called SCode. SCode is the internal representation used by the MIT/GNU Scheme interpreter. The "sf" program also performs a handful of optimizations, such as user-directed beta substitution and early binding of known variables such as CAR. * "compiler" contains the native-code compiler. This program translates SCode to machine-language instructions.
コンパイラーの威力ってどのぐらい?
無料で使えるなら、いいねボタンを押しちゃうぞ。
こういう時は、例のやつですな。 フィボナッチで各種言語をベンチマーク
(define (fib n) (if (< n 2) n (+ (fib (- n 2)) (fib (- n 1))))) (write (fib 38)) (newline)
Lisp/Scheme系なら、竹内さんちのたらい回しの方が、日本的情緒に溢れていた良いかも。
sakae@uB:~/mit$ time mit-scheme --load fib.scm --eval '(quit)' MIT/GNU Scheme running under GNU/Linux : ;Loading "fib.scm"...39088169 ;... done [1]+ Stopped mit-scheme --load fib.scm --eval '(quit)' real 1m38.176s user 0m0.000s sys 0m0.004s
mit-schemeはスクリプトをロード(実行)後、replに落ちるんだけど、そこで(exit)すると 終了してもいいかいなんて聞いてくる。これじゃ、時間測定には向かないので(quit)で、 バックグランドに廻るようにしてる。よって、測定終了後、fgしてから手動で終了の事。
1 ]=> (cf "fib.scm") ;Generating SCode for file: "fib.scm" => "fib.bin"... ; This program does not have a USUAL-INTEGRATIONS declaration. ; Without this declaration, the compiler will be unable to perform ; many optimizations, and as a result the compiled program will be ; slower and perhaps larger than it could be. Please read the MIT ; Scheme User's Guide for more information about USUAL-INTEGRATIONS. ;... done ;Compiling file: "fib.bin" => "fib.com"... done ;Unspecified return value
翻訳したぞ。ちゃんと指定すると、小さく速くなるとな。 下記の成果物が出来た。
sakae@uB:~/mit$ ls fib.bci fib.bin fib.com fib.scm
fib.bciはdebug用の情報。fib.binはSCodeと呼ばれるもの。CLで言うfaslだかの高速ロード用 中間コード。そしてfib.comがネイティブコードらしい。
sakae@uB:~/mit$ time mit-scheme --load fib.com --eval '(quit)' MIT/GNU Scheme running under GNU/Linux : ;Loading "fib.com"...39088169 ;... done [2]+ Stopped mit-scheme --load fib.com --eval '(quit)' real 0m3.661s user 0m0.000s sys 0m0.004s
インタープリタに比べて劇速になった。先ほど指示が有った宣言
(declare (usual-integrations))
を追加して、再コンパイルしてみる。
1 ]=> (cf "fib.scm") ;Generating SCode for file: "fib.scm" => "fib.bin"... done ;Compiling file: "fib.bin" => "fib.com"... done ;Unspecified return value
今度は、文句を言われなかった。で、御利益は有るのか?
sakae@uB:~/mit$ time mit-scheme --load fib.com --eval '(quit)' : ;Loading "fib.com"...39088169 ;... done : real 0m2.207s user 0m0.000s sys 0m0.000s
Oh! 大分速くなったな。こんなに良いものなら、デフォで採用してよね。最適化が 不都合なら、その時は最適化不要って宣言の方がよくね?
参考までにgaucheでも。
sakae@uB:~/mit$ time gosh fib.scm 39088169 real 0m9.879s user 0m9.516s sys 0m0.068s
そして、自分自身で、実行時間を測るやつ
1 ]=> (with-timings (lambda () (fib 38)) (lambda (run-time gc-time real-time) (write (internal-time/ticks->seconds run-time)) (write-char #\space) (write (internal-time/ticks->seconds gc-time)) (write-char #\space) (write (internal-time/ticks->seconds real-time)) (newline))) 97.67 1.97 99.898 ;Value: 39088169
ここまでは良い。でも、コンパイルして出来上がったfib.comって、ネイティブ言語になってますって事は、 Windowsへ持って行ったら、そのまま実行出来るかもよ。なんたって、*.com は *.exe の 仲間と思ってますから。
やってみた。16bit MS-DOS Subsystemって窓が出てきて言う事にゃ、The NTVDM CPU has encountered an illegal instraction. CS:05d4 IP:01cc OP:fo 4c c5 a0 d8 ... との事でした。Windowsって、サフィックスでころっと 騙かされるのね。
しょうがないので、mit-schemeの窓から、(load "fib.com")したら、ちゃんと結果を返して くれた。mitの人達よ。紛らわしいサフィックスは勘弁してよね。まあ、mit-scheme上なら、 バイナリーでも互換性が有るって事が分かったから、今回は許したる。(と、偉そうに言ってみた)
debug環境
mit-schemeは、きっとRMSの弟子達が作ったに違いない。結構debug環境が充実してる。
sakae@uB:~/mit$ cat bug.scm ;; bug (define (f n) (if (zero? n) "0" (* n (f (- n 1))))) (f 5)
こんなbug入りのやつを、edwinから実行してみる。
(load "bug") ;Loading "bug.scm"... ;The object "0", passed as the second argument to integer-multiply, is not the \ correct type. ;To continue, call RESTART with an option number: ; (RESTART 2) => Specify an argument to use in its place. ; (RESTART 1) => Return to read-eval-print level 1. ;Start debugger? (y or n): y ;Starting debugger...
エラーを検出すると、debuggerを使うか聞いてくる。はいと答えると、画面が反転して (Windows上だと、別窓が出現)debug用画面になる。上面の状態を下に示す。
The buffer below shows the current subproblem or reduction. ----------- The error that started the debugger is: The object "0", passed as the second argument to integer-multiply, is not the\ correct type. >S0 (integer-multiply 1 "0") R0 (* n (f (- n 1))) R1 (if (zero? n) "0" (* n (f (- n 1)))) R2 (f (- n 1)) S1 (f (- n 1)) R0 (* n (f (- n 1))) R1 (if (zero? n) "0" (* n (f (- n 1)))) R2 (f (- n 1)) :
いわゆる、スタックフレームだ。Ctl-n, Ctl-p で、このフレーム内を登ったり下ったり 出来る。
下画面は、そのフレームでの細かい内容が表示される。debug画面からの脱出はqだ。元のscheme画面に 戻れる。
最初の画面にも出てるけど、(RESTART 2)すると、エラーしてる式の値を新しいものに置き換えて、 継続出来る。
(restart 2) ;... done ;Value: 120
(restart 2) Ctl-x Ctl-e で式を評価すると、mini-bufferにNew Argumentって聞いてくるので、 この場合なら、1って入力((integer-multiply 1 "0")の実行結果が1でしたって宣言)。 これで、エラーが無くなって、計算が継続。最終結果が得られた。
Edwinからのオペレーションだったけど、普通にREPLを起動して、bugでエラーが起こった時は、 (debug)って叩けば、テキスト版のdebuggerが起動するよ。Schemeマシーンだな。
mitからの依頼
Windows上のEdwinで、find-fileした時に、読み込んだはずのファイル内容が、なにも表示されない。 バッファーリストで確認すると、サイズがゼロになってる。メッセージバッファーに何か 出てるかと思ったけど、そんなバッファーは存在しない。
しょうがないので、開いたバッファー上で編集し、save-bufferとかwrite-fileすればちゃんと、 ファイルが作成される。
また、dired-modeからファイルをオープンしても、画面には何も表示されない。dired-mode では、ちゃんとファイルサイズを認識してるにも関わらずにね。不思議な現象。
リナの方では全く問題ないので、Window上のedwinがおかしいと思われ。何が原因か? mitからの 宿題と言うか調査と言うか挑戦状を突きつけられました。
こういう時は、ソース嫁。それはいいんですが、相手はschemeですぜ。すいすいとソースの 海を泳ぐのどうします?
cscopeはこの場合使えんな。etagsも駄目、となると、オイラーのかすかな記憶では、 GNU GLOBALかな。プラグインを入れると、 色々な言語も扱えるらしい。色々な言語のうちの一つにschemeが挙がってた。
実際はGNU GLOBALの対応言語を大幅に増やすPygmentsパーサーを導入する って事をしないと駄目みたい。世の中甘くないね。
諦めて、grepで代用するか。で、edwinのソースの中に入って、まずはどんなファイルが 有るか調査。rf822.scmってSMAP解散との一部報道でSMTPが話題に? じゃないですか。(元の鞘に収まったけど、次の危機は9月だかの契約終了時?)他にもnntp.scmなんてのが有ったり、vc-*.scmなんてので、バージョン管理は お任せあれ(*には、gitとかcvsとかがマッチする)とか、emacsに一歩でも近づきたい気持ちが 現れてますなあ。おかげで、調査人は大変だけど。。。
で、そんな中、Tags.shなんてのに気付いた。中身は
#!/bin/sh etags *.scm
えっ!! 早速、man etags そこには驚愕な事実が。。。
DESCRIPTION The etags program is used to create a tag table file, in a format un‐ derstood by emacs(1); the ctags program is used to create a similar ta‐ ble in a format understood by vi(1). Both forms of the program under‐ stand the syntax of C, Objective C, C++, Java, Fortran, Ada, Cobol, Er‐ lang, Forth, HTML, LaTeX, Emacs Lisp/Common Lisp, Lua, Makefile, Pas‐ cal, Perl, PHP, PostScript, Python, Prolog, Scheme and most assem‐ bler-like syntaxes.
思い込みは悪ですなあ。そしてオイラーの知らないtagsの使い方が、こちらで紹介されてた。 Emacs の tags 機能の使い方
Dive into edwin source
ソースの海に潜ってみます。まずは、find-fileがどうなってるかだな。
(define (find-file filename) (select-buffer (find-file-noselect filename #t))) (define-command find-file "Visit a file in its own buffer. If the file is already in some buffer, select that buffer. Otherwise, visit the file in a buffer named after the file." "FFind file" find-file)
define-commandって、edwinに特有なのかなあ。次はfind-file-noselect
(define (find-file-noselect filename warn?) : (let ((buffer (new-buffer (pathname->buffer-name pathname)))) (let ((error? (not (catch-file-errors (lambda (condition) condition #f) (lambda () (read-buffer buffer pathname #t)))))) :
次は、read-buffer かな。
(define (read-buffer buffer pathname visit?) : (lambda () (set! truename (->truename pathname)) (%insert-file (buffer-start buffer) truename visit?) : (if visit? (begin (set-buffer-pathname! buffer pathname) (set-buffer-truename! buffer truename) (set-buffer-save-length! buffer) (buffer-not-modified! buffer) (undo-done! (buffer-point buffer)))) :
次は、%insert-file あたりかなあ?後ろの方に、set-buffer-save-length! なんてのが 有るからね。
(define (%insert-file mark truename visit?) (let ((method (read-file-method (mark-group mark) truename))) (if method (method truename mark visit?) (let ((do-it (lambda () (group-insert-file! (mark-group mark) (mark-index mark) truename)))) (if (ref-variable read-file-message mark) (let ((msg (string-append "Reading file \"" (->namestring truename) "\"..."))) (temporary-message msg) (let ((value (do-it))) (temporary-message msg "done") value)) (do-it))))))
やっと、それっぽいのに辿り付いたな。read-file-methodの方を追って行ったら、gzipが どうのとか暗号ファイルがどうのと言われたので、バックトラック。もう一方の道、 group-insert-file! を追う。
(define (group-insert-file! group start truename) (call-with-input-file truename (lambda (port) (if (not (ref-variable translate-file-data-on-input group)) (port/set-line-ending port 'NEWLINE)) (let ((length ((port/operation port 'LENGTH) port))) (bind-condition-handler (list condition-type:allocation-failure) (lambda (condition) condition (error "File too large to fit in memory:" (->namestring truename))) (lambda () (without-interrupts (lambda () (prepare-gap-for-insert! group start length))))) (let ((n (let ((text (group-text group)) (end (fix:+ start length))) (let loop ((i start)) (if (fix:< i end) (let ((n (input-port/read-substring! port text i end))) (if (fix:> n 0) (loop (fix:+ i n)) (fix:- i start))) length))))) (if (fix:> n 0) (without-interrupts (lambda () (let ((gap-start* (fix:+ start n))) (undo-record-insertion! group start gap-start*) (finish-group-insert! group start n))))) n)))))
やっと辿り付いたって感じ。なお、現在地は、fileio.scmです。
本当にここへ寄ってくれているか、debugプリントしたいんだけど、 どうしたら良いの? プリントしなくても、 bkpt でも、いいんだけど。
もう一つ、難問。Windowsのそれには、*.bci ファイルしか置いてない。これって、そのまま 使えるの? ちょっと実験。
1 ]=> (load "fib.bci") ;Loading "fib.bci"... ;Unbound variable: compressed-b1-1.00 ;To continue, call RESTART with an option number: ; (RESTART 3) => Specify a value to use instead of compressed-b1-1.00. ; (RESTART 2) => Define compressed-b1-1.00 to a given value. ; (RESTART 1) => Return to read-eval-print level 1.
単独では無理のようだ。edwin-dirの中に、edwin.ldrとかedwin.pkgとかが付いてて、 これでパッケージに仕立て上げているけど、その中の何処かで定義されてるのかな?
試しにウブ上で作ったfileio.bciをWindows側へ持って行った。サイズが異なっていたけど、 取り合えず気にしない。で、Windows側で試すも、相変わらずfind-fileで読み込んだサイズは ゼロ。勿論表示せず。
ひょっとして、libの下にあるやつはおまけかと思い、そろりそろりと、libの下のedwinを ディレクトリィー毎削除。ちゃんとedwinは起動する。やっぱりおまけだ。動作に寄与してない。
どんどんlibの下を削除していったら、all.comとかが無いとmit-schemeは起動しない事が 判明。もうわけわかめなので、入れていた9.2版を9.1版に変更。やっぱりfind-fileは 動作せず。もうさじを投げ出そう。
ふと悪あがきで、fileio.scmをロードして、オリジナルの物を上書きしたらいいんでないかいと 思った。実際にやってみると、同ファイル以外で定義されたものが必要と言われ、動作せず。 どうやら、パッケージとして出来上がっているようで、そいつらにちょっかいを出すってのは、 無理みたい。残念至極であります。
うぶ上で使えばよい。それも不自由なedwin経由じゃなくて本物のemacs経由でね。 M-x run-schemeすると、入れているgaucheに競り勝って、mit-schemeが起動してくるよ。
bugを踏んだ時の対処法
1 ]=> (f 10) ;The object "0", passed as the second argument to integer-multiply, is not the \ correct type. ;To continue, call RESTART with an option number: ; (RESTART 2) => Specify an argument to use in its place. ; (RESTART 1) => Return to read-eval-print level 1. 2 error> (debug) There are 14 subproblems on the stack. Subproblem level: 0 (this is the lowest subproblem level) Expression (from stack): (integer-multiply 1 "0") There is no current environment. The execution history for this subproblem contains 3 reductions. You are now in the debugger. Type q to quit, ? for commands. 3 debug> z 1 z Cannot evaluate in current environment; using the read-eval-print environment instead. Expression to EVALUATE and CONTINUE with ($ to retry): ;Value: 3628800
gaucheとmit-schemeが競り合って、mit-schemeが勝つのは、オイラーが軟弱で、debug環境を こよなく愛しているのを知ってるからだな。gaucheのdebug環境が充実するのは何時だろう?
readme
Gauche で IE を制御する (ことが出来たらよかったのだけど)
Gauche-OLE を使って IE に内トロコイド曲線を書く
CentOS 7のシステム管理「systemd」をイチから理解する
この人、本当にHaskell初心者かなあ? Schemeとかガンガン使ってて、Haskellにも手を 出してみましたって構図かな。Haskellな人はDebugどうしてるんだろう? せめてmit-scheme ぐらいなdebug環境が欲しいと思うのは、オイラーだけ? 違うって、Haskellでやる事は、 ひたすらオイラーの100問を解くだけですから、debugなんて関係ないのです。既に脳内debugが 完了してますから。
でも、最近FinTechとかいう造語をよく聞くぞ。ファイナンスとITが融合したとかいう あおり。前に人参をぶら下げられれば、誰もが目の色を変えますよ。そんな人はHaskellだろうと ocamlだろうと、根性debugしてるに違いありません。
ウォール街がこの手の関数型言語に侵されたのは、有り余る頭脳がNASAから放出されて、 数学が得意な連中がなだれ込んだと言われていますよ。