maximaとか(4)

町の公民館から、10月より始まる文化講座の案内が来た。

パソコン・エクセル入門講座(2時間/回 X 5回)ですって。50歳以上の方限定。料金は、 資料代として、500円のみ。講師は、NPO何とかの方らしい。

どんな内容なのだろう? エクセルじゃ、年賀状とは相性が悪いし(そうでもないか、住所録 なんてのには利用出来そうだな。)、きっと、体重でも測って肥満にならないようにグラフを 付けましょうかな? それとも、おいらがPythonでやった、血圧管理表でも作るのかな?

女房には、『あんた暇なんだから、シルバーセンターに、パソコン講師の登録をすれば』なんて 言われてるんで、一度、潜入してみよーかな。500円で、10時間も暇潰し出来るなんて、 そこらのパチンコ屋も真っ青ですよ。

パソコンは、講座の方で用意してくれるらしい。中古のパソコンが、だーと用意してあるの だろうか? NPOなんとかが主催するなら、財政事情がおぼつかないだろうから、きっと そうなんだろう。肝心のエクセルはどうなんだろう? 元々、パソコンに付属してたのを 使うのかな? 単体で買うと、結構するからなあ。

財政難のため、エクセルは、ジェネリック品を使います。 昔は、オープンオフィスだったけど、ぼらくるのごたごたを嫌って、フォークした一派が やってるのが人気みたいだ。

LibreOfficeが、日本のアジト。使う予定は余り無いんだけど、 記念にWindows版を落としてきた。ああ、どこでもオフィスと言う ポータブル版も有るのね。 会津若松市で採用されたみたいだけど、どこでも仕事出来まっせって、こりゃまた大変だな。

いやいや、そんな事は無いっす。会津の人は、頑固で粘り強いから、きっと家では、 ちびちびやりながら、ばりばりと 仕事をなさっている事でしょう。会津の酒は いろいろあれど、おいらは栄川を良く飲んでたなあ。つまみは、これまた、馬刺し (あの人が聞いたら何と野蛮な人と思うでしょう)や棒鱈とかいろいろ あるけど、おいらは、ニシンの山椒漬けが好物でした。あーー、よだれが出てきたよ。

マクロとか

前回、timeを使って実行時間を計ってみた。資料によるとこのtimeもマクロだそうだ。この際だから 本場のマクロを覗いてみる。どのコモンリスプにするかちと迷ったけど、リスプの先輩に敬意を表して 、GCLにする。今はGCLになってるけど、生まれも育ちに京都どすぇって事で、KCLだ。

(defmacro time (form)
  (let ((real-start (gensym)) (real-end (gensym)) (gbc-time-start (gensym))
        (gbc-time (gensym)) (x (gensym)) (run-start (gensym)) (run-end (gensym))
        (child-run-start (gensym)) (child-run-end (gensym)))
  `(let (,real-start ,real-end (,gbc-time-start (si::gbc-time)) ,gbc-time ,x)
     (setq ,real-start (get-internal-real-time))
     (multiple-value-bind (,run-start ,child-run-start) (get-internal-run-time)
       (si::gbc-time 0)
       (setq ,x (multiple-value-list ,form))
       (setq ,gbc-time (si::gbc-time))
       (si::gbc-time (+ ,gbc-time-start ,gbc-time))
       (multiple-value-bind (,run-end ,child-run-end) (get-internal-run-time)
         (setq ,real-end (get-internal-real-time))
         (fresh-line *trace-output*)
         (format *trace-output*
                 "real time       : ~10,3F secs~%~
                  run-gbc time    : ~10,3F secs~%~
                  child run time  : ~10,3F secs~%~
                  gbc time        : ~10,3F secs~%"
                 (/ (- ,real-end ,real-start) internal-time-units-per-second)
                 (/ (- (- ,run-end ,run-start) ,gbc-time) internal-time-units-per-second)
                 (/ (- ,child-run-end ,child-run-start) internal-time-units-per-second)
                 (/ ,gbc-time internal-time-units-per-second))))
       (values-list ,x))))

ちょっと長いけど、そんなに難しくはなさそうだ。最初のletで、局所変数名を作ってる。名前が 衝突しないように、gensymでユニークな名前を付けてるな。次のletの局所変数部で、変数を初期化 してる。

get-internal-real-timeなんてのは、ハードの時計を読むしかけだろう。初期化が終わったら、 (setq ,x (multiple-value-list ,form))で、被測定物を動かしてるんだな。それが終わったら、 再び時計を読んで、後は計算しながら表示か。 そんなに難しい事はないな。

ついでなので、clispでtimeマクロがどう実装されてるか見ておく事にしよう。

;; ----------------------------------------------------------------------------
(defun gensym-list (how-many)
  (map-into (make-list (if (numberp how-many) how-many (length how-many)))
            #'gensym))
;; ----------------------------------------------------------------------------
(defmacro time (form)
  (let ((vars (gensym-list 9)))
    `(MULTIPLE-VALUE-BIND ,vars (%%TIME)
      ;; this construction uses stack space at run time only
      (UNWIND-PROTECT ,form (MULTIPLE-VALUE-CALL #'%TIME (%%TIME) ,@vars)))))

こちらは、随分すっきりしてるな。9個もローカル引数を使ってるのに、あっさりとリストに しちゃってるのがすっきりさせるコツなのね。 所で、UNWIND-PROTECT って、何するものぞ? あの分厚い仕様書は手元に無いしなあ。

Hyperspec

そうだ、そういう時には、マニュアルだな。委員会Lispだと、きっと仕様書めいたものが 公開されているだろう。調べてみたよ。そしたら有った。

SLIMEからちゃんとオンラインマニュアルを引けるようになってた。 オンラインマニュアルって 言っても、資料を手元に置いて使う方式だけどね。

tar玉を 落としてきて適当な所に展開する。それから、その在り処を、.emacsに書いてあげる。 表示はWebでって事なので、Windowsならデフォのブラウザーが開かれるんだな。

unix系だと定番はw3m。生憎emacsから駆動するモジュールが入っていなかったんで、www/emacs-w3mを 入れようとしたら、余計な物まで取り込んじゃう連鎖が始まった。面倒なので、野良build したよ。 ああ、取ってくる先は、http://emacs-w3m.namazu.org/ ね。そんでもって、設定は これぐらいね。

;;;;;;;; Common lisp hyper-spec --> C-c C-d h
(setq common-lisp-hyperspec-root
    (concat "file://" (expand-file-name "~/.HyperSpec/"))
      common-lisp-hyperspec-symbol-table
      (expand-file-name "~/.HyperSpec/Data/Map_Sym.txt"))
(require 'w3m)
(setq browse-url-browser-function 'w3m-browse-url)

引きたい関数の上にカーソルを合せて、C-c C-d h すれば、瞬時に説明が表れる。これは便利。 やっぱり委員会Lispだけあるなあ。これをScheme系でやろうとすると、えらい苦労する。 Schemeは俺様schemeになってるんで、心中する気でやらんとならん。

再びmaxima

どうもいろいろ寄り道しちゃったので、本道へ戻ります。maximaはLispでどのように実装されて るか? 熱いmaxima本(pdf)をめくっていると、いろいろ書いてあるんだけど、やっぱり自分で SE(ソース探検)するのが一番だな。何たって、目の前にソースが有るんですから。

share/srcの中にソースが眠っている。176本のLispファイルだ。総行数は12万行を超える。 アメリカの東海岸で生を受け、Lispの揺り篭で育てられ、途中で養子に出されると言う数奇な 運命を辿っているうちに、どんどんと成長したんだな。

はて、どこから見ていく? maxima本みたいにつまみ読みもいいけど、おいらは正攻法で 行ってみよー。まずは、どうやって動き出すかだな。ユーザーと対話しながら動いているんで REPLが有るかななんて思うんだけど。。。しばし探偵気分で地道に捜査してく。

[sakae@cdr ~]$ lv /usr/local/bin/maxima
   :
elif [ "$MAXIMA_LISP" = "sbcl" ]; then
    exec sbcl --dynamic-space-size 512 
      --core "$maxima_image_base.core" 
      --noinform $MAXIMA_LISP_OPTIONS 
      --end-runtime-options 
      --eval '(cl-user::run)' 
      --end-toplevel-options "$arg1" "$arg2" "$arg3" "$arg4" "$arg5" "$arg6" "$arg7" "$arg8" "$arg9"

ちょいと見易いように整形してますが、どうやら、エントリーポイントは、(cl-user::run)みたいです。 そんじゃ、Lispのコードからそれを探してみます。

[sakae@cdr /usr/local/share/maxima/5.25.0/src]$ lv init-cl.lisp
;;********************************************************
;; file:        init-cl.lisp
;; description: Initialize Maxima
;; date:        Wed Jan 13 1999 - 20:27
;; author:      Liam Healy <Liam.Healy@nrl.navy.mil>
;;********************************************************

(in-package :maxima)

;;; An ANSI-CL portable initializer to replace init_max1.lisp
      :
(defun cl-user::run ()
  "Run Maxima in its own package."
  (in-package :maxima)
  (setf *load-verbose* nil)
  (setf *debugger-hook* #'maxima-lisp-debugger)
  ;; See discussion on the maxima list
  ;; http://www.math.utexas.edu/pipermail/maxima/2011/024014.html.
  ;; Set *print-length* and *print-level* to some reasonable values so
  ;; that normal Lisp structure is shown, but prevent typical circular
  ;; structures from hanging Lisp.
  ;;
      :
    (catch 'to-lisp
      (initialize-real-and-run-time)
      (intl::setlocale)
      (set-locale-subdir)
      (adjust-character-encoding)
      (set-pathnames)
      (when (boundp '*maxima-prefix*)
        (push (pathname (concatenate 'string *maxima-prefix*
                                     (if *maxima-layout-autotools*
                                         "/share/locale/"
                                         "/locale/")))
              intl::*locale-directories*))
      (setf (values input-stream batch-flag)
            (process-maxima-args input-stream batch-flag))
      (loop
         (with-simple-restart (macsyma-quit "Maxima top-level")
           (macsyma-top-level input-stream batch-flag))))))

海軍の兵隊さんが顧問Lispポータブルになるように、書き換えたとな。体力じゃなくて知力な お仕事も軍隊にはあるのね。まあ、最近はサイバー戦争とかロボット使って地球の裏側から攻撃 ってのが流行りですから、そのはしりだったんでしょうな。

で、この関数ではループでぐるぐるですかい。ループ構文も(無駄に)奥が深いらしいけど、ここでは 単純ループなのね。 この関数のすぐ下に、こんなのも有った。

(defun $to_lisp ()
  (format t "~&Type (to-maxima) to restart, ($quit) to quit Maxima.~%")
  (let ((old-debugger-hook *debugger-hook*))
    (catch 'to-maxima
      (unwind-protect
           (maxima-read-eval-print-loop)
        (setf *debugger-hook* old-debugger-hook)
        (format t "Returning to Maxima~%")))))

(defun to-maxima ()
  (throw 'to-maxima t))

(defun maxima-read-eval-print-loop ()
  (setf *debugger-hook* #'maxima-lisp-debugger-repl)
  (loop
     (catch 'to-maxima-repl
       (format t "~a~%~a> ~a" *prompt-prefix* (package-name *package*) *prompt-suffix*)
       :

これ、何処かでお世話になったなあ。こんな所に居たのね。

で、macsyma-top-level は、何処かと探してみれば、

[sakae@cdr /usr/local/share/maxima/5.25.0/src]$ lv macsys.lisp
    :
(defun macsyma-top-level (&optional (input-stream *standard-input*) batch-flag)
  (let ((*package* (find-package :maxima)))
    (if *maxima-started*
        (format t (intl:gettext "Maxima restarted.~%"))
        (progn
          (if (not *maxima-quiet*) (maxima-banner))
          (setq *maxima-started* t)))

こうやって追って行ってもいいんだけど、どうも道を逸れている感じがする。はて、どうしたものか? かくなる上は強引に。。。

(%i3) to_lisp();

Type (to-maxima) to restart, ($quit) to quit Maxima.

MAXIMA> (break)

debugger invoked on a SIMPLE-CONDITION: break

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE    ] Return from COMMON-LISP:BREAK.
  1: [MACSYMA-QUIT] Maxima top-level
  2:                Ignore runtime option --eval "(cl-user::run)".
  3: [ABORT       ] Skip rest of --eval and --load options.
  4:                Skip to toplevel READ/EVAL/PRINT loop.
  5: [QUIT        ] Quit SBCL (calling #'QUIT, killing the process).

(COMMON-LISP:BREAK "break")
0] backtrace

0: (COMMON-LISP:BREAK "break")
1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (BREAK) #<NULL-LEXENV>)
2: (MAXIMA-READ-EVAL-PRINT-LOOP)
3: ($TO_LISP)
4: (MEVAL1 #<unavailable argument>)
5: (MEVAL (($TO_LISP)))
6: (MEVAL* (($TO_LISP)))
7: (TOPLEVEL-MACSYMA-EVAL (($TO_LISP)))
8: (CONTINUE #<unavailable argument> #<unavailable argument>)
9: (MACSYMA-TOP-LEVEL #<unavailable argument> #<unavailable argument>)
10: (RUN)
11: (SB-INT:SIMPLE-EVAL-IN-LEXENV (RUN) #<NULL-LEXENV>)
12: (SB-IMPL::PROCESS-EVAL/LOAD-OPTIONS ((:EVAL . "(cl-user::run)")))
13: (SB-IMPL::TOPLEVEL-INIT)
14: ((LABELS SB-IMPL::RESTART-LISP))

動いているやつを強引に止めちゃった。テヘェ、こういう無茶が出来るんで楽しい。正に 生体解剖ですな。で、

(defun toplevel-macsyma-eval (x) (meval* x))

これが、諸悪(違うってば)の根源ですか。

(%i1) to_lisp();

Type (to-maxima) to restart, ($quit) to quit Maxima.

MAXIMA> (trace toplevel-macsyma-eval)

(TOPLEVEL-MACSYMA-EVAL)
MAXIMA> (to-maxima)
Returning to Maxima
(%o1)                                true
(%i2) x
;
  0: (TOPLEVEL-MACSYMA-EVAL $X)
  0: TOPLEVEL-MACSYMA-EVAL returned $X
(%o2)                                  x
(%i3) diff(x**2 + 3 * x + 4, x);
  0: (TOPLEVEL-MACSYMA-EVAL
      (($DIFF) ((MPLUS) ((MEXPT) $X 2) ((MTIMES) 3 $X) 4) $X))
  0: TOPLEVEL-MACSYMA-EVAL returned ((MPLUS SIMP) 3 ((MTIMES SIMP) 2 $X))
(%o3)                               2 x + 3

ヘェー、meval* を通過すると、式が変形されて、それが再評価されてるって事なんだな。

君のゆく道は、果てしなく遠い
なのになぜ 歯をくいしばり
君は行くのか そんなにしてまで

1966年に発表された、『若者たち』って曲を思い出しちゃったじゃないですか。

追いかけて行ったら、やっとmaxima本と整合が取れる所まできた。

(defmfun meval (form)
  (simplifya (meval1 form) nil))

この後、maxima本をトレースして

如何でしょうか.  数式の積分は計算機の中の大勢の小人さん達が捻り鉢巻で計算し
ていると感じていた方も多いかもしれませんが, 実際の処理は数値計算で方程式を解
くときに色々な定式化に基いて計算処理を行うのと同様に, 数式処理でも定式化に基
づいて記号の処理を行っているのです. 「計算機の中に小人が居る」といったロマン
ティックなことは残念ながらありません.

と言う、実もふたも無い現実を直視すべきだろうか?