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から放出されて、 数学が得意な連中がなだれ込んだと言われていますよ。