maxima 観光
とある日、田舎道を走っていると、前から来た大型ダンプからパッシングを食らった。嫌がらせ? それともオイラーの車に不具合(後ろから火を噴いて今にも離陸しそうとか)でも有って、親切に教えてくれたのかな? ダッシュボードのメーター類を確認するも、異常なし。
更に走り慣れた道を進んで行くと、今度は軽トラからもパッシングを受けた。ははんと思って、スピードを落としたよ。
今までいやと思う程、この道を走っているけど、ネズミ捕りなんてやってなかったぞ。年末のボーナスを稼いで来いと尻を叩かれた署員が稼ぎに出動してるんだな。
ゆっくり走っていると、見えてきましたね。檻に誘導する旗を持った署員が。 親切なダンプと軽トラの運転手さん、ありがとう。ゴールド免許を汚さずに済みました。
【警察のネズミ捕り】取り締まりが行われる道と時間帯はほぼ決まっている!
パッシングの意味とやり方とは?知らないと大事故に巻き込まれるかも?
公務執行妨害ねぇ。そう言えば、散歩中にネズミ捕りの現場に出くわした事があるぞ。しげしげ観察。ホーンが有ったな。その前で立ち止まって、10GHzの電波を浴びる勇気は無かったぞ。ヘタレですから。(HAMな時代に1.2GHzの波を浴びた事は有ります。)
お仕事ご苦労様です。今日の稼ぎはいかほどですか? 頑張ってくださいと激励すればよかったかな?
また、あの現場を散歩コースに入れておこう。
ecl
前回はmaximaを作るんで色々やった。maximaはlispで書かれているんで、sbclやgclやclispや、高価な事で有名なアレグロCLとかで動く。組み込み用のeclでも動く。
組み込み用は、メモリーがタイトなやつとかだ。rubyに対するmrubyみたいなものね。(ちょいと、例えが悪いか)
どうやって実現してるかと言うと、lisp語をC語に変換して、後はgccなりclangに任せてしまえと言う戦略。ここまでは、朧な知識として知ってたけど、具体的にはどうするの? そりゃ、本山へ登って修行しろでしょうな。下記が本山。
そして、こちらが、秘儀、変換の巻だ。
ob$ ls -ltr total 60 -rw-r--r-- 1 sakae wheel 47 Oct 20 15:46 hello.lisp -rwxr-xr-x 1 sakae wheel 14880 Oct 20 15:47 hello.fas* -rw-r--r-- 1 sakae wheel 1352 Oct 20 15:49 hello.o -rwxr-xr-x 1 sakae wheel 11776 Oct 20 15:50 myecl* ob$ ldd myecl myecl: Start End Type Open Ref GrpRef Name 1b3a7000 3b3a9000 exe 2 0 0 myecl 0bd39000 2bd61000 rlib 0 1 0 /usr/local/lib/libecl.so.6.0 021b3000 221b6000 rlib 0 2 0 /usr/local/lib/libgmp.so.10.0 039bf000 239da000 rlib 0 2 0 /usr/local/lib/libgc.so.4.0 0bd16000 2bd19000 rlib 0 2 0 /usr/local/lib/libffi.so.1.2 02e1e000 22e21000 rlib 0 3 0 /usr/lib/libpthread.so.26.1 04912000 24915000 rlib 0 2 0 /usr/lib/libm.so.10.1 09a51000 29a63000 rlib 0 1 0 /usr/lib/libc.so.95.0 0848e000 28491000 rlib 0 1 0 /usr/local/lib/libatomic_ops.so.2.0 05881000 05881000 ld.so 0 1 0 /usr/libexec/ld.so
出来上がった実行ファイルmyeclは、そこそこ小粒になってる。その成分には、lispの核、gmpなる多倍長精度ライブラリー、ゴミ掃除用の業者とかが必要。これらは各アプリで共通に使えるから、まあアプリでメモリーを圧迫するって事は避けられる方向だな。
これ Common Lisp用だけど、scheme用は無いの? そういう貴方には、gambitが有るよ。 暫く触っていないから、どんな進化が有るか、後で確かめてみたいな。それから速さを求めるなら、あれが有ったな。と、あれ、それしか出てこないのは、老化進行中だからであります。
探検用の地図
さて、maximaを探検したい。取り合えずの目標は、sin(%pi/4); した時に、綺麗な無理数で答えが返ってきた。この頭がどうなってるか知りたいんだ。そして、それを煎じて薬にしたいって訳。老化防止策の一環ですな。
いきなり山に挑む(深海を夢想してもいいけど)遭難するんで、現地調査と言うか、資料を漁ってみる。
こんな資料がひっそりと置かれていた。
/home/sakae/src/maxima-5.43.0/doc/implementation/
紐解いてみると、maximtoplevelflow.png ってグラフが置いてあった。(この場合のグラフってのは、折れ線グラフやら棒グラフじゃなくて、繋がりを表す図ね)
firefoxなりで開いてみたら、年寄りには辛いサイズの絵だった。強引に拡大してみると、汚い絵が迫ってくるだけ。遠目には、いかにもって感じに見えるだけど、近寄ると肌が荒れて見えるのと一緒。
何とか整形して、近寄っても綺麗に見たいものだ。綺麗な絵好きです。勿論、綺麗なおねーさんもね。原稿が置いてあったんで、拡大に耐えられるような図を作ればいいんだな。そういう場合は、馬鹿の一つ覚えで、svgフォーマットしょ。
テキストデータをグラフ画像に変換するツール「Graphviz」ことはじめ を使って、綺麗なおねーさん、もとえ絵にしたよ。
debian:implementation$ dot -Tsvg maximatoplevelflow.dot -o my.svg
これで、バッチグーと古語な感嘆符ですよ。
runしてトップレベル内からcontinueに入るとな。そやつが、maxima語を読み込んで、評価して 表示するっていう風に動いてる。continueなんて説明してるけど、read-eval-print-loopじゃん。まあ、言語系は例外なく、こういう作りだから、驚くにはあたらない。
which continue
要になりそうな、continueをsrcの下で探してみる。
(base) [sakae@c8 src]$ grep continue *.lisp | grep defun macsys.lisp:(defun continue (&key ((:stream input-stream) *standard-input*) batch-or-demo-flag one-shot) suprv1.lisp:(defun continuep ()
最初、検索文字にcontinueだけを指定したら、それを呼び出している所とかも引っかかってきたので、定義されてる所って語句も指定した。いかにもってファイル名の所で定義してんのね。
(base) [sakae@c8 src]$ grep -- '(continue' *.lisp macsys.lisp: (continue :stream (make-echo-stream fileobj *standard-output*) macsys.lisp: (continue :stream input-stream :batch-or-demo-flag batch-flag) mload.lisp: (catch 'macsyma-quit (continue :stream in-stream :batch-or-demo-flag demo)) suprv1.lisp: (when (and slowp (cdr l1) (not (continuep)))
こちらは、呼び出し元。以外な事に、あちこちに有る。文脈によって動きが違う、複雑な作りになってるのかな? lispを振り回す人は、他人を振り回す傾向にある頭脳明晰な人が多いから、軌跡を追うのは大変そう。(と、早弱気ですよ)
準備体操 地図のインデックス作成
あちこち飛び回る予定なので、tagsを作っておこう
(base) [sakae@c8 src]$ etags *.lisp
continueを開いてみたら、案の定、色々なlispに対応するコードが書かれていて、かつ長ったらしくて読む気が失せた。ここから、下位のルーチンに潜って行こうとするも、糸口すら掴めない。出鼻から道に迷った気分。
そこら辺をうろうろしてたら、面白そうな関数に出会った。
(defmfun $build_info () (or *maxima-build-info* (setq *maxima-build-info* (let ((year (sixth cl-user:*maxima-build-time*)) :
bug_reportを提出する時にも使われる、大事な関数だ。なお、関数の頭に$マークが付いているのは、maxima語をlisp語で表現した時の約束らしい。ちょいと試してみるか。
(base) [sakae@c8 ~]$ maxima -q (%i1) build_info(); (%o1) Maxima version: "5.43.0" Maxima build date: "2019-10-15 15:05:30" Host type: "x86_64-unknown-linux-gnu" Lisp implementation type: "SBCL" Lisp implementation version: "1.5.6" User dir: "/home/sakae/.maxima" Temp dir: "/tmp" Object dir: "/home/sakae/.maxima/binary/5_43_0/sbcl/1_5_6" Frontend: false
ふむ、確かにこの関数が対応してるようだ。そうすると、この関数が呼ばれた時にdebuggerに入ってbreakさせる。そしてバックトレースすれば、実行の道筋が露わになるな。
後は、その道筋に沿って、tagsと頼りに、あっちにフラフラ、こっちにフラフラすればいいな。
それから、後注意しなきゃいけなさそうな事として、全体がパッケージになってる事だな。
準備体操 debuggerの使い方
debuggerの出番だな。生憎Common Lisp系のdebuggerって良く知らないのよ。そんな訳で、下記で体力と言うか知力を付ける事にした。
(defun fact (n) (if (zerop n) (/ 1 0) (* n (fact (- n 1))))) (defun te (n) (format t "Fact is ~A ~%" (fact n)))
こいつを C-c C-l でロードすると、ZERO割を検出してSTYLE-WARNIGが出て来る。強引に(te 3)で実行してみると、
arithmetic error DIVISION-BY-ZERO signalled Operation was (/ 1 0). [Condition of type DIVISION-BY-ZERO] Restarts: 0: [RETRY] Retry SLIME REPL evaluation request. 1: [*ABORT] Return to SLIME's top level. 2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1003A29BC3}>) Backtrace: 0: (SB-KERNEL::INTEGER-/-INTEGER 1 0) 1: (FACT 1) 2: (FACT 2) 3: (FACT 3) 4: (TE 3) 5: (SB-INT:SIMPLE-EVAL-IN-LEXENV (TE 3) #<NULL-LEXENV>) 6: (EVAL (TE 3)) --more--
ちゃんとエラーを検出してバックトレースが出てきた。ZERO割を修正、factに入ってすぐに(break)を突っ込み。そうしておいて、factの所でC-c C-cしてfactを再コンパイル。(te 3)で呼び出し。
Backtrace: 0: (FACT 3) 1: (TE 3) :
0の所にカーソルを持って行って、cで継続もしくはsでステップ実行。
Backtrace: 0: (FACT 1) 1: (FACT 2) 2: (FACT 3) 3: (TE 3) :
大体、使い方は分かった。おまけで、こんな事も出来る
CL-USER> (step (te 3))
Evaluating call: (TE 3) With arguments: 3 [Condition of type STEP-FORM-CONDITION] Restarts: 0: [STEP-CONTINUE] Resume normal execution 1: [STEP-OUT] Resume stepping after returning from this function 2: [STEP-NEXT] Step over call 3: [STEP-INTO] Step into call 4: [RETRY] Retry SLIME REPL evaluation request. 5: [*ABORT] Return to SLIME's top level. --more-- Backtrace: 0: ((LAMBDA ())) 1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LET ((SB-IMPL::*STEP-OUT* :MAYBE)) (UNWIND-$ 2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (STEP (TE 3)) #<NULL-LEXENV>) 3: (EVAL (STEP (TE 3))) --more--
何かの時に使えるかも知れないな。
この強力なdebugger機構はそれぞれのCommon Lispに組み込まれている。SBCLなら、 5 Debuggerに解説が有る。
それともう一つ忘れてならないのは、emacsと共に使うslimeと言うツール。このツールのおかげで、各種Lispの癖を吸収して共通の操作が出来るようになっている。
とは言え、CommonLispを入れ、emacsを入れその上にslimeを被せると言うと、初心者は尻をまくって逃げ出すに違いない。逃げ出さないように、 Portacle is a complete IDE for Common Lisp、こんな便利なリスプマシンが提供されてるのね。USBに入れておけば、いつでもどこでも使えるぜい。
昔からlispの環境は親切、それを真似てSmallTalkなんかも、便利な対話環境を準備したとか。 言語仕様のみならず、環境まで考えて進歩してきたLispは凄いなあ。感心しきりです。
なお、上記のレッスンで触れられていた、debugを調子良く行うための宣言(C語で言うなら、コンパイル時に指定する、-g 相当)は、 src/maxima.asd って、言う、パッケージの作成仕様(Makefileみたいなものか)に書かれている。上記のように、バックトレースが出来なかったら、要変更だな。
モダンCommon Lisp第3回: SLIMEの使い方 基礎編
SLIME: The Superior Lisp Interaction Mode for Emacs
run maxima
さあ、いよいよmaximaを走らせよう。どうやって? 出来たらslime環境からが望ましい。そんな我が儘もあろうかと、ちゃんと準備スクリプトが用意されてる。そう、コンパイルした時に使った、maxima-build.lisp 。これ長ったらしいので、必要最小限に切り詰めた。
何が邪魔かと言うと、色々なlispの個性を吸収するためのコードが入ってるんだ。Common (共通) Lispと言う割には、ちっとも共通じゃ無いんだよ。
debian:src$ cat zzz.lisp (load "../lisp-utils/defsystem.lisp") (defun maxima-compile () (mk:oos "maxima" :compile)) (defun maxima-load () (mk:oos "maxima" :load)) (defun maxima-dump () #+sbcl (sb-ext:save-lisp-and-die "binary-sbcl/maxima.core" :toplevel #'cl-user::run) #-(or clisp sbcl gcl cmu scl allegro lispworks ccl) (format t "Sorry, I don't know how to dump an image on this Lisp")) (maxima-load)
冒頭でロードされてるdefsystem.lispも、共通性を担保してパッケージを作成する為の保証器みたいな物だ。これ少々古臭いやり方だ。最近はquicklispとか言うパッケージング方式が主流とか。pythonで言うと、setup.pyは古いからpipを使おうぜと言う乘りだな。
兎も角、ある物は使おう。そして名前も分かり易いけど、長ったらしいので、オイラー流にキーを叩き易いものにした。そして、最後に、メモリーにコードを展開させといた。
だから、このファイルをロードするだけで、maximaの実行準備は整う。後は、上で目星を付けておいた関数にbreakを仕込んで、その関数だけ再コンパイル。
そしてやおら(run) すると、maximaのプロンプトが出てくる。そこで、build_info(); すれば、debuggerに落ちてくれる。
Backtrace: 0: (MAXIMA::$BUILD_INFO-IMPL) 1: (MAXIMA::MEVAL1 ((MAXIMA::$BUILD_INFO))) 2: (MAXIMA::MEVAL ((MAXIMA::$BUILD_INFO))) 3: (MAXIMA::MEVAL* ((MAXIMA::$BUILD_INFO))) 4: (MAXIMA::TOPLEVEL-MACSYMA-EVAL ((MAXIMA::$BUILD_INFO))) 5: (MAXIMA::CONTINUE :STREAM #<SWANK/GRAY::SLIME-INPUT-STREAM {99187B81}> :BA$ 6: (MAXIMA::MACSYMA-TOP-LEVEL #<SWANK/GRAY::SLIME-INPUT-STREAM {99187B81}> NI$ 7: (RUN) 8: (SB-INT:SIMPLE-EVAL-IN-LEXENV (RUN) #<NULL-LEXENV>) 9: (EVAL (RUN)) 10: (SWANK::EVAL-REGION "(run) ..) :
呼び出しの階層が見えたな。後はこのルートに沿って、tagsで作った地図を片手にあちこち飛び回ればいい。
上記で6の所にカーソルを持って行って、リターンを叩くと、下記のように環境を確認出来る。 もう一度リターンを叩くと、展開されたものが引っ込む。
6: (MAXIMA::MACSYMA-TOP-LEVEL #<SWANK/GRAY::SLIME-INPUT-STREAM {99187B81}> NI$ Locals: BATCH-FLAG = NIL INPUT-STREAM = #<SWANK/GRAY::SLIME-INPUT-STREAM {99187B81}> Catch-tags: MAXIMA::MACSYMA-QUIT MAXIMA::CONTINUE MAXIMA::QUIT-TO-LISP
M-n, M-p は、次々にそれをやってくれる。ざっと眺め見るには便利。
enter debugger other way
slime配下だと面白い事が出来る。 CL-USER> が出てる所で、C-c C-l してzzz.lispなるローダーもどきを実行。更に maximaを走らせておいてから、C-c C-c で割り込みをかけてみる。btの結果を見ると
12: ((FLET SWANK/BACKEND:CALL-WITH-LOCK-HELD :IN "/home/sakae/.emacs.d/elpa/sl$ 13: ((:METHOD STREAM-READ-CHAR (SWANK/GRAY::SLIME-INPUT-STREAM)) #<SWANK/GRAY:$ 14: (READ-CHAR #<SWANK/GRAY::SLIME-INPUT-STREAM {99138B81}> NIL (NIL) #<unused$ 15: (MAXIMA::DBM-READ #<SWANK/GRAY::SLIME-INPUT-STREAM {99138B81}> NIL (NIL) N$ Locals: EOF-ERROR-P = NIL EOF-VALUE = (NIL) REPEAT-IF-NEWLINE = NIL STREAM = #<SWANK/GRAY::SLIME-INPUT-STREAM {99138B81}> 16: (MAXIMA::CONTINUE :STREAM #<SWANK/GRAY::SLIME-INPUT-STREAM {99138B81}> :BA$ 17: (MAXIMA::MACSYMA-TOP-LEVEL #<SWANK/GRAY::SLIME-INPUT-STREAM {99138B81}> NI$ 18: (RUN)
こんな風に以外な関数名(dbm-read)なんて所で、待ちに入っている事が分かる。読み込んだ文字を内部のDBと照合でもしてるんですかね?
それから、15のdbm-readが、上のフレームのどのあたりにあるか確認したくなるな。そういう時は、16にカーソルを移動(n,p)して、v すれば、ソースが開かれて対象行がブリンクする。 大体中央に表示されるけど、前後に移動しちゃって対象を見失った場合、vすれば、また中央に引き戻してくれる。
vでソースの窓を出しておいて、M-n,M-pすると、ソースも追従するのね。
ソースが出てる時、linum-modeで行番号を付ければレポートに最適。どのファイルに有るかも ステータス行に出てるしね。
201 (tagbody 202 top 203 (setq r (dbm-read input-stream nil eof)) 204 ;; This is something of a hack. If we are running in a server mode
この走ってるアプリを止めて、中を覗くってgdb attachと同じ事をやってるんだね。ただlispの凄い所は、この状態でソースを変更して、関数だけ再コンパイル出来ちゃう事。ダイナミックな環境だからこそ許される特技だな。
shiroさんは、これをやったけど、ソースに反映するのを忘れて泡喰ったなんて事を、昔伺った事があるな。(smalltalkも同じ悩みがある。メモリー上でパッチをすいすい当てられるけど、パッチ後のソースが無くて青くなったって話をよく聞く)
それから、今回は作者の厚意により実装のグラフが提供されてて、それからヒント(行き当たりばったりの感がするけど)を得られた。そういう厚意が無い場合、どうする?
10秒考えて、プロンプトを出してる所を、頑張って見つけるかなあ。見つかった所あたりが、グルグル回る所だろうからね。
sinの簡略化
三角関係を担当してるやつとして、名前から trigi.lisp,trigo.lispが浮かんできた。 まずはtrigi.lispを見て行くと、
;; Simplified shortcuts for constant expressions. (defvar %pi//4 '((mtimes simp) ((rat simp) 1 4.) $%pi)) (defvar %pi//2 '((mtimes simp) ((rat simp) 1 2) $%pi)) (defvar sqrt3//2 '((mtimes simp) ((rat simp) 1 2) ((mexpt simp) 3 ((rat simp) 1 2)))) (defvar -sqrt3//2 '((mtimes simp) ((rat simp) -1 2) ((mexpt simp) 3 ((rat simp) 1 2))))
冒頭に、こんな怪しげなやつが出てきた。何処かで使われるだろうと思ったら、trigoの方でちらっと使われてた。
別の戦略、simp-%sin とかがある。名前からして簡略化だろう。網を張ってみるかな。
sin(%pi/4);
Backtrace: 0: (MAXIMA::SIMP-%SIN ((MAXIMA::%SIN) ((MAXIMA::MTIMES MAXIMA::SIMP) (# 1 4) $ 1: (MAXIMA::MEVAL* ((MAXIMA::%SIN) ((MAXIMA::MQUOTIENT) MAXIMA::$%PI 4))) 2: (MAXIMA::TOPLEVEL-MACSYMA-EVAL ((MAXIMA::%SIN) ((MAXIMA::MQUOTIENT) MAXIMA$ Locals: X = ((MAXIMA::%SIN) ((MAXIMA::MQUOTIENT) MAXIMA::$%PI 4)) Catch-tags: MAXIMA::RAT-ERR 3: (MAXIMA::CONTINUE :STREAM #<SWANK/GRAY::SLIME-INPUT-STREAM {10038804D3}> :$
ふむ、この段階で、関数と引数が分離されているとな。
Backtrace: 0: (MAXIMA::SIMP-%SIN ((MAXIMA::%SIN) ((MAXIMA::MTIMES MAXIMA::SIMP) (# 1 4) MAXIMA::$%PI)) 1 NIL) Locals: FORM = ((MAXIMA::%SIN) ((MAXIMA::MTIMES MAXIMA::SIMP) ((MAXIMA::RAT MAXIMA::SIMP) 1 4) MAXIMA::$%PI)) Y = 1 Z = NIL
後は、この周辺を嗅ぎ回ればいいのだな。
easy way
simp-%sinの所を見ていたら、怪しげなやつをその内部に見つけた。%piargs-sin/cos て関数ね。名前の途中に記号が入ってるなんて、他の言語では許されないだろうけど、Lisp界隈では当たり前の事だ。
で、この関数にもbreakを仕込んでみる? 面倒そう。そもそもemacsの上でslimeを動かすってのは、普通の人には参入障壁が大きすぎる。トランプ野郎から文句を言われるに違いない。
そこで、オイラーは考えた。emacsとかを使わないで内部を探る方法を。CLには、traceって言う便利なdebugのお供が付いている。この使用例として、階乗の関数がどう動くか観察してみましょうってのがある。
別の使い方として、指定した関数を通過したか確認出来るんだ。関数に入った時の引数の値、関数から脱出する時の値を報告してくれる。勿論、今使ってるsbclにも標準装備してるんで、やってみる。
(base) [sakae@c8 src]$ sbcl --load zzz.lisp This is SBCL 1.5.6, an implementation of ANSI Common Lisp. : ; caught 1 STYLE-WARNING condition * (trace simp-%sin) WARNING: COMMON-LISP-USER::SIMP-%SIN is undefined, not tracing. NIL * (trace maxima::simp-%sin) (MAXIMA::SIMP-%SIN) * (run) Maxima 5.43.0 http://maxima.sourceforge.net using Lisp SBCL 1.5.6 Distributed under the GNU Public License. See the file COPYING. Dedicated to the memory of William Schelter. The function bug_report() provides bug reporting information. (%i1)
sbclにローダーを渡して、maximaのコードをメモリー上に展開。sbclはプロンプトが * って言う素っ気ないもの(普通はslime上で作業をするから誰も気にしない)。
そこで、traceしたい関数を指定。今居るパッケージ上(COMMON-LISP_USER)には、そんな関数は無いと警告を受けた。maximaって言うパッケージ名をコロン2個挟んで指定する。コロン1個だと、exportされた物のみ指定出来る。 準備が整ったので、(run)で、maximaを起動。
(%i1) sin(%pi/4); 0: (MAXIMA::SIMP-%SIN ((MAXIMA::%SIN) ((MAXIMA::MTIMES MAXIMA::SIMP) ((MAXIMA::RAT MAXIMA::SIMP) 1 4) MAXIMA::$%PI)) 1 NIL) 0: SIMP-%SIN returned ((MEXPT SIMP) 2 ((RAT SIMP) -1 2)) 1 (%o1) ------- sqrt(2)
0:の行が2つあるけど、最初が入った時、後が出た時。確かに通過してるね。
それじゃ、怪しそうな関数 %piargs-sin/cos が、呼び出されているか、更にtrace候補に追加する。
(%i2) to_lisp(); Type (to-maxima) to restart, ($quit) to quit Maxima. MAXIMA> (trace %piargs-sin/cos) (%PIARGS-SIN/COS) MAXIMA> (to-maxima) Returning to Maxima (%o2) true (%i3) sin(%pi/4); 0: (MAXIMA::SIMP-%SIN ((MAXIMA::%SIN) ((MAXIMA::MTIMES MAXIMA::SIMP) ((MAXIMA::RAT MAXIMA::SIMP) 1 4) MAXIMA::$%PI)) 1 NIL) 1: (MAXIMA::%PIARGS-SIN/COS ((MAXIMA::MTIMES MAXIMA::SIMP) ((MAXIMA::RAT MAXIMA::SIMP) 1 4) MAXIMA::$%PI)) 1: %PIARGS-SIN/COS returned ((MEXPT SIMP) 2 ((RAT SIMP) -1 2)) 0: SIMP-%SIN returned ((MEXPT SIMP) 2 ((RAT SIMP) -1 2)) 1 (%o3) ------- sqrt(2)
maximaから一端lispに移り、そこでtraceに設定。それからmaximaに戻って実行。
今度は、0: に続いて、わずかにネストして、1: の行が出現してる。simp-%sinの中から、%piargs-sin/cosが呼ばれているのが分かる。
こういう方法なら、障壁も無いだろうから、普通のユーザーでも取り掛かれるよ。(ああ、普通のユーザーは、こういう事をしようなんて思わないか)
my memo
slimeが起動してて、見たいパッケージの中に移動してると、
M-. で、定義元へ飛べる (no need etags) M-, で、元の場所に戻れる slime-who-calls” (C-c C-w C-c にバインドされています)で、これは関数が呼ばれている 全ての場所を表示 slime-who-macroexpands (C-c C-w RET) はマクロが使われている全ての場所を表示する slime-who-references (C-c C-w C-r) はそれの変数用バージョン