maxima 観光

とある日、田舎道を走っていると、前から来た大型ダンプからパッシングを食らった。嫌がらせ? それともオイラーの車に不具合(後ろから火を噴いて今にも離陸しそうとか)でも有って、親切に教えてくれたのかな? ダッシュボードのメーター類を確認するも、異常なし。

更に走り慣れた道を進んで行くと、今度は軽トラからもパッシングを受けた。ははんと思って、スピードを落としたよ。

今までいやと思う程、この道を走っているけど、ネズミ捕りなんてやってなかったぞ。年末のボーナスを稼いで来いと尻を叩かれた署員が稼ぎに出動してるんだな。

ゆっくり走っていると、見えてきましたね。檻に誘導する旗を持った署員が。 親切なダンプと軽トラの運転手さん、ありがとう。ゴールド免許を汚さずに済みました。

【警察のネズミ捕り】取り締まりが行われる道と時間帯はほぼ決まっている!

パッシングの意味とやり方とは?知らないと大事故に巻き込まれるかも?

ネズミ捕りいるよパッシングでセーフ!--;

公務執行妨害ねぇ。そう言えば、散歩中にネズミ捕りの現場に出くわした事があるぞ。しげしげ観察。ホーンが有ったな。その前で立ち止まって、10GHzの電波を浴びる勇気は無かったぞ。ヘタレですから。(HAMな時代に1.2GHzの波を浴びた事は有ります。)

お仕事ご苦労様です。今日の稼ぎはいかほどですか? 頑張ってくださいと激励すればよかったかな?

また、あの現場を散歩コースに入れておこう。

ecl

前回はmaximaを作るんで色々やった。maximaはlispで書かれているんで、sbclやgclやclispや、高価な事で有名なアレグロCLとかで動く。組み込み用のeclでも動く。

組み込み用は、メモリーがタイトなやつとかだ。rubyに対するmrubyみたいなものね。(ちょいと、例えが悪いか)

どうやって実現してるかと言うと、lisp語をC語に変換して、後はgccなりclangに任せてしまえと言う戦略。ここまでは、朧な知識として知ってたけど、具体的にはどうするの? そりゃ、本山へ登って修行しろでしょうな。下記が本山。

Embeddable Common-Lisp

そして、こちらが、秘儀、変換の巻だ。

1.6. Compiler examples

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って良く知らないのよ。そんな訳で、下記で体力と言うか知力を付ける事にした。

Debugging Lisp Part 1: 再コンパイル

(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:SLDB

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) はそれの変数用バージョン

etc

第2回 ニューラルネットワーク最速入門 ― 仕組み理解×初実装(中編)