maximaとか(8)

遠くの山に雪が降ったというので、紅葉見物を兼ねて近くの温泉へ行ってきた。

生憎、里に近い所だったので紅葉前線は下降してなかったけど、ほのかに色付き初めていた。 (心眼で色を診るとかね)

渓流の音を聞きながら、露天風呂に入るのって何年ぶりだろう? 実に爽快で日本に生まれて良かった なあと思える。首から下はぬくぬく、頭は寒いからのぼせる事もなく、いつまでも入っていられる。

露天風呂が飽きたので、今度は天空の温泉とかに行った。谷間からえっちら、おっちらと階段を 登り、着いた所は、檜の湯船。誰も居なくて、温泉を一人占め。湯船に浸かりながら、今登って きた谷を見下ろすと、渓流が結構速いスピードで流れていた。

こういう谷には、岩魚でもいるのかなあ? 温泉に浸かりながら魚釣りなんて出来るんだろうか? 魚が無理でも、電波ぐらいは釣れるかな?

Windows用のgrep

リスプのソース見るのに、Windows上のemacsを使ってみたんだけど、M-x grep で呼び出すgrepの 結果が利用出来ないんだ。unix上では、結果の所にそれぞれアンダーラインが引かれて、htmlの リンクのごとく参照出来るんで便利なんだけどね。

何でWindows上ではそうならないかと思ったら、grepの結果表示がunixのそれと違う事に気づいた。 何処かにいるgrepが変態仕様なんだな。不幸なOSには、which なんて装備してないし、一生懸命に Windowsにその場検索させましたよ。

そしたら、何とgrepはボーランドのCコンパイラの付属品で付いてきたものでした。こやつをgnu系の やつに差し替えればいいんだな。ググル様に、vectior grep ぐらいの検索ワードで問い合わせ。

五つ星のgrepが冒頭に出てきた。こやつpdfでもwordでもバリバリ検索するんで大人気みたい。 でも、今回の用途には全く不向き。おいらはコマンドラインから使えるやつが欲しいんや。 コマンドライン用は数える程しか無かったよ。Winddowsでソフト開発とかの真似事する人って みんなM$のSDKなのね。

適当に見繕って入れたら、やっとemacsからgrepが使えるようになった。やれやれ。

#.

前に出てきた、#. って何物ってのが有ったんだけど、やっと疑問が氷解した。ええ、ぐぐる様に いろいろと問い合わせをしてみたんだけど、ひっかからないのね。この業界では常識で誰も書かない のかねぇ。

LOLのリードマクロの所に説明が出ていた、ずばり、読み込み時のevalですって。普通のマクロが 動き出す前、ソースを読み込んだ時に、検知してS式をエバッちゃう身のこなし。後に証拠は 残さず。リードマクロって、みんなこういう早業が身上だ。こういうのまとめて解説してる 所が無いかと探してみたけど、やっぱり無かったね。

こうなったら、gaucheのinfoのsection 4 にある、字句構造(拡張された#構文)あたりの方が 頼りになるな。shiroさんが言ってるように共通リスプからいっぱい輸入しましたですから。 それでも分からない時は、ソース嫁。

sbcl/code/sharpm.lispとreader.lispあたりが、その集積地のようです。

;;;; conditional compilation: the #+ and #- readmacros

(flet ((guts (stream not-p)
         (unless (if (let ((*package* *keyword-package*)
                           (*read-suppress* nil))
                       (featurep (read stream t nil t)))
                     (not not-p)
                     not-p)
           (let ((*read-suppress* t))
             (read stream t nil t)))
         (values)))

  (defun sharp-plus (stream sub-char numarg)
    (ignore-numarg sub-char numarg)
    (guts stream nil))

  (defun sharp-minus (stream sub-char numarg)
    (ignore-numarg sub-char numarg)
    (guts stream t)))

  (set-dispatch-macro-character #\# #\+ #'sharp-plus)
  (set-dispatch-macro-character #\# #\- #'sharp-minus)

これ、C言語の#ifとその反対語(何と言う形容)部分だ。説明は簡潔に、;;;; に有るだけ。

SHARPM~1.LIS:19:;;;; reading arrays and vectors: the #(, #*, and #A readmacros
SHARPM~1.LIS:123:;;;; reading structure instances: the #S readmacro
SHARPM~1.LIS:194:;;;; reading numbers: the #B, #C, #O, #R, and #X readmacros
SHARPM~1.LIS:238:;;;; reading circular data: the #= and ## readmacros
SHARPM~1.LIS:354:;;;; conditional compilation: the #+ and #- readmacros
SHARPM~1.LIS:374:;;;; reading miscellaneous objects: the #P, #\, and #| readmacros
SHARPM~1.LIS:435:;;;; a grab bag of other sharp readmacros: #', #:, and #.

察して下さいってか?それはそうと、read-evalマクロのルーチンは何処に有るんかな? 単純なgrep では見つけられんかった。ちょいと悔しいので、実験。

CL-USER> (macroexpand-1 '`(prog  #.(format t "Hello") t))
Hello
'(PROG () T)
NIL

Helloって表示してるのは、リーダーマクロの仕業。次の行の'(PROG () T) は、展開結果。formatは もう既に消え失せているって事だな。

CL-USER> (macroexpand-1 '`(prog (format t "fuga") t))
'(PROG (FORMAT T "fuga") T)
NIL

こちらは、リーダーマクロ無しの場合。評価も行われず、字面がそのまま出てきました。

LOLによると、このread-eval-macro は、セキュリティー的に超危険なため、使うなら最大の 防御を施した上で覚悟して使えって警告してます。read-eva-macro以外でも、普通のevalがソース上に出てくるのは、 何かおかしい、再考せよのシグナルと捕らえよとも述べています。

set-dispatch-macro-charactor

上で出てきた関数(でいいのかな)は、読んで字のごとく、2文字のマクロキャラクターを 登録するのだった。簡単な例として、gaucheのデバックプリント #? は、次のようになる。

(set-dispatch-macro-character #\# #\?
                              #'(lambda (stream ch1 ch2)
                                  (let ((c (read-char stream)))
                                    (cond ((eq c #\=) `(debug-print ,(read stream)))
                                          (t (error "Illegal #?..."))))))
(defun debug-print (x) (print x))

世の中には好きな人が居るもんで SBCLのリーダを上書きして『超リードマクロ』を実装 しちゃった人とか、 最高にキモい Lisp コードを書いてみよう with 100 行リーダーマクロ で、存在感を顕示されてました。おいらは、まだヘタレですから、その境地には達していません。

save-lisp-and-die

前回だったか、maximaの作り方を見てた時、(Lisp)coreを作る関数として、sbclの場合は、 save-lisp-and-dieってのがある事を知った。そして興味深い事に、:EXECUTABLE と言うkeyが ある事に眼が行っていた。

これって、単独(シングル・バイナリー)のファイルに出来るって事? よくLisp板とかを 見てると、実行形式にして配れますかって質問が上げられている。Lispでアプリを書いたは いいが、みんなに使ってもらおうとしたら、まずLispの処理系をインストールして下さいって 所が、多いなる障壁として立ちはだかってくるんだな。全くもって同意しますだ。

あわよくば、一つのファイルに収まって、可搬なら言う事ないですよ。これはトライしてみる鹿。

CL-USER> (defun main ()
           (format t "Hello SBCL~%"))
MAIN
CL-USER> (main)
Hello SBCL
NIL
CL-USER> (save-lisp-and-die "myapp" :toplevel #'main :executable t)

slimeの上で実行するとsbclが自殺しちゃうんで接続が切れてしまう。また、Windows上のsbclだと マルチスレッドで動いているんで、save出来ませんとか言われる。よって、コンソールから 立ち上げたsbclで実行するのがお勧め。

[undoing binding stack and other enclosing state... done]
[saving current Lisp image into myapp:
writing 2752 bytes from the read-only space at 0x01000000
writing 1712 bytes from the static space at 0x01100000
writing 31268864 bytes from the dynamic space at 0x58000000
done]

上記は、sbclがらのダイイング・メッセージでした。 そんじゃ、単独で実行出来るか確認。

[sakae@cdr ~]$ ./myapp
Hello SBCL
[sakae@cdr ~]$ file myapp
myapp: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically linked (uses shared libs), for FreeBSD 8.0 (800107), not stripped
[sakae@cdr ~]$ ldd ./myapp
./myapp:
        libm.so.5 => /lib/libm.so.5 (0x280ab000)
        libc.so.7 => /lib/libc.so.7 (0x280c5000)

ちゃんと実行出来ました。変なものもリンクしてないし、普通のアプリのようです。念には念を 入れて、変なものを読み込んでいないか、確認しておきます。

[sakae@cdr ~]$ truss ./myapp |& grep open
open("/etc/libmap.conf",O_RDONLY,0666)           ERR#2 'No such file or directory'
open("/var/run/ld-elf.so.hints",O_RDONLY,00)     = 3 (0x3)
open("/lib/libm.so.5",O_RDONLY,00)               = 3 (0x3)
open("/lib/libc.so.7",O_RDONLY,027757760414)     = 3 (0x3)
open("/usr/share/locale/ja_JP.UTF-8/LC_COLLATE",O_RDONLY,0666) = 3 (0x3)
open("/usr/share/locale/ja_JP.UTF-8/LC_CTYPE",O_RDONLY,0666) = 3 (0x3)
open("/usr/share/locale/ja_JP.UTF-8/LC_MONETARY",O_RDONLY,05004607015) = 3 (0x3)
open("/usr/share/locale/ja_JP.UTF-8/LC_NUMERIC",O_RDONLY,043) = 3 (0x3)
open("/usr/share/locale/ja_JP.UTF-8/LC_TIME",O_RDONLY,010) = 3 (0x3)
open("/usr/share/locale/ja_JP.UTF-8/LC_MESSAGES",O_RDONLY,0540) = 3 (0x3)
open("/usr/home/sakae/myapp",O_RDONLY,05007140200) = 3 (0x3)
open("/usr/home/sakae/myapp",O_RDONLY,00)        = 3 (0x3)
open("/dev/tty",O_RDWR,0666)                     = 4 (0x4)
[sakae@cdr ~]$

どうやら問題なさそうです。が、問題は

[sakae@cdr ~]$ ls -lh myapp
-rwxr-xr-x  1 sakae  kuma    30M 10  6 09:53 myapp*

その図体の大きさです。たかが、Hello で、30Mとは、その大富豪っぷりに苦笑いしましょう。 とか言いながら、ファイルの正体が not stripped である事にさりげなく眼を光らせて いたんだな。ええい、ストリップしちゃえば、すっきりするよ。

[sakae@cdr ~]$ strip myapp
[sakae@cdr ~]$ ls -lh myapp
-rwxr-xr-x  1 sakae  kuma   125K 10  6 10:07 myapp*

激ヤセしました。これは期待が持てるぞ。そんじゃ実行してみっか。

[sakae@cdr ~]$ ./myapp
This is SBCL 1.0.34, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* (* 1 2 3 4 5)

120

ありゃま、普通のsbclになっちゃたよ。痩せても動くように生命維持装置はしっかりDNAとして 保持してたのね。太っちょを解剖してみたら面白いかも?と一瞬考えたけど、多分Lispの脂しか 見えないと思う。スリムなやつでちょっと確認してみっか。

[sakae@cdr ~]$ strings myapp | grep /
  :
/usr/local/lib/sbcl/
/sbcl.core
  :

もう、このぐらいにしとくかな。

そんでもってIEしてたら、こんな簡単なmainだと問題は無いんですが、複雑になるとそれなりの苦労があるようで、 専用の補助マクロを公開されてる方に 出会いました。

my maxima

そんじゃ、maximaに戻って、ezunits等がmaxima起動時から直ぐに使えるように、俺様用の やつを作ってみます。

(%i1) load(physical_constants);
(%o1)
    /usr/local/share/maxima/5.25.1/share/contrib/ezunits/physical_constants.mac
(%i2) load(units);
(%o2)       /usr/local/share/maxima/5.25.1/share/physics/units.mac
(%i3) (10 ` kg) * (17 ` m/s^2);
                                   kilogram meter
(%o3)                        170 ` --------------
                                          2
                                         s

大丈夫そうなので、lispに落ちてから、ダンプします。

(%i4) to_lisp();

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

MAXIMA> (sb-ext:save-lisp-and-die "fatmaxima" :toplevel #'cl-user::run :executable t)
[undoing binding stack and other enclosing state... done]
[saving current Lisp image into fatmaxima:
writing 2752 bytes from the read-only space at 0x01000000
writing 1712 bytes from the static space at 0x01100000
writing 52658176 bytes from the dynamic space at 0x58000000
done]
[sakae@cdr ~]$

凄い太っちょだと予想してましたが、半端じゃないですね。

[sakae@cdr ~]$ ./fatmaxima
Maxima restarted.
(%i5) (x ` m) / (y ` s);
                                   x   meter
(%o5)                              - ` -----
                                   y     s

起動直後から、ezunitsが使えました。こうやって、どんどんと太らせればいいんだな。 プロンプトが途中から始まっているのは、ご愛嬌だな。ま、いいか。

(%i5) i: 3 ` amp;
                                      coulomb
(%o5)                             3 ` -------
                                      second
(%i6) v: 100 ` volt;
(%o6)                             100 ` volt
(%i7) v / i;
                               100   second volt
(%o7)                          --- ` -----------
                                3      coulomb
(%i8) 50 ` ohm;
                                    second volts
(%o8)                          50 ` ------------
                                      coulomb

これ、電気の世界ではお馴染みな単位だけど、ちょっと抵抗があるなあ。そんなんで、IEしてたら いろいろな事情があるらしい事が分かった。

なぜ SI 単位系で 1 `kg をユニット単位とするのか

解りにくい MKSA 単位系となった経緯

足を踏み入れるべきか、迷いますなあ。