calc and gforth
散歩に出ようとしたら、セリがあのスーパーで特売してるから買ってきてと、 女房から下命が下った。毎日、ipadでチェックをしてますからね。
で、スーパーへ行ってみると、黒山の人だかりには、なっていなかった。 ひとりのおばちゃんが、一生懸命にセリを吟味中。
既に籠には、10束ぐらいは入っているのに、更に追加の様子。そんなに買って どうされるんですか? って、いらぬ質問を思わずしちゃったぞ。
おばちゃんに言うには、セリの味噌漬を作るのよとの事。なかなか、手に入ら ないので、大量に作っておくらしい。
1週間程、つけておくと、食べ頃になり、数ヶ月は大丈夫らしい。そんなに食べ たら、塩分で体に悪いんじゃ?
サラダとかのトッピングにしたりして、少しづつ食べるらしい。
そこから、おばちゃんの弾丸トークが炸裂しましたよ。声をかけたのは、こち らなんで、途中で逃げだす訳にもいかず、じっと我慢の子を装っていましたよ。 おかげで、何が話されたかは、忘却の彼方。
家に帰って、セリの味噌漬って知ってるって女房に聞いたら、美味しいのよね とのたまう。未だ、女房は、ぼーと生きてるね。おばちゃんの知恵がつくのは、 何時になるのだろう?
calc
電磁気学を見ていたら、マクスウェルの電磁方程式が 出てきてて、
誘電率(e) 8.8540e-12 透磁率(u) 1.2566e-6 v = 1 / sqrt( e * u)
誘電率と透磁率から、電磁波の速度(v)が求まるそうな。電磁波も光も同じだから、 関係無いような物と結びついているんだな。emacsで確認してみる。
M-x calc
8.8540e-12 1.2566e-6 * Q 1 TAB / ===> 299800058.661
数値を2個入れて(space)区切りでよい。* で、それの積をとり、Qで、sqrtし てあげる。次に1をいれてから、TABで、数値の順番を入れ替え、/ で、割り算。
8.8540e-12 1.2566e-6 * Q & ===> 299800058.661
こちらは、逆数(1/x)を一発で求められる、invを使ってみた。 慣れると便利だな。
The GNU Emacs Calculator(ja) を見ながら、復習しておく。
stack
RET TOPの複製 dup DEL TOPの破棄 drop TAB TOPと次の入れ替え swap _ 負数の入力 ' 代数的入力 '3+4*2 $ TOPの値 U 最後の演算のundo D redo
data type
2:3 分数 2/3 '(1,2) 複素数 '1+2i 複素数 1@ 2' 3" 1時2分3秒 時間が整数として扱われるので、時間の加減が可能 '<1 2 2019> 2019年1月2日 日を基数として加減が可能
月日は数字で入力しても、米国風で表示されるんで焦らないで。
日付演算が手軽にできるって面白いな。実践投入の良い例がないかなと脳内バッ ファを検索。姪の所で去年の11月に男子が誕生した。この間、面会と言うか見 てきた。赤子と言うけど、正に赤い顔をしてたな。今日まで何日経過してるの かな? 今日の日付と誕生日を積んで、引き算。
--- Emacs Calculator Mode --- |Emacs Calculator Trail 1: 68 |alg' <Sat Feb 2, 2019> . |alg' <Mon Nov 26, 2018> | ->68
抱かせてもらったけど、ずっしり重くて腕がしびれた。母はずっと抱いて世話 するんだから、知らず知らずのうちに逞しくなるのも頷けますよ。
ふと、自分の心臓が生れてこのかた、何回鼓動してきたか計算してみたりして。 驚愕な結果にびっくりしてます。2G回、その間に血液はどれぐらい輸送され ていたか? 一回のドッキンで、60ccらしいですよ。ドラム缶何本分?
行列
べクトルと行列もサポートしてる。 ベクトルを作るには、'[1 2 4 5] のようにするのが基本。いきなり、[ して から、数値を入力していき、最後に ] で、閉じてもよい。 行列も同様。
octaveで慣れ親しんだ行列作成方法も可能。'[1 2; 3 4] で、
1: [ [ 1, 2 ] [ 3, 4 ] ]
が、作成される。
足し算や掛け算、逆行列は、それぞれ +, *, & で計算出来る。 行列式は、V D とか。
べクトルが出てきたら、統計関数も調べておく。 h s するとサマリーが出てくるので、そこで検索。 統計の基本用語meanあたりを手掛りに探すと、集落が見付かる。 たとえば、
The ‘u S’ (‘calc-vector-sdev’) [‘vsdev’] command computes the standard deviation of the data values. If the values are error forms, the errors are used as weights just as for ‘u M’. This is the _sample_ standard deviation, whose value is the square root of the sum of the squares of the differences between the values and the mean of the ‘N’ values, divided by ‘N-1’.
u # count u + sum u X max u N min u M mean H u M median u S sdev
graph
calcは裏でgnuplotを動かして簡単にグラフを描く事ができる。
基本は、Xの値を格納したベクトルとYの値のベクトルをスタックに積んでから、 g f するだけ。g gとかすると、グリッドをon/offできる。
2: [0 .. 360] 1: sin(x)
こんな設定にすると、1周期分のサイン波が描かれる。荒い分解能と思ったら、g Nして、分解能を決定できる。たとえば、g N 360とすれば、1度毎に計算して くれる。
数学用なんで、折れ線グラフのみだ。(ひょっとして、棒グラフ等も可能かも 知れないけど、調べていない)
X値をベクトルじゃなくて、1とかにすると、それを初期値として自動で増加す るようになっている。
じゃ、既にあるデータでグラフを描くときは、どうする? 毎度おなじみの、 血圧データの入った、CSVファイルに登場願おう。
emacs current.csvしてCSVファイルを表示させる。次に取り込みたい範囲を指 定する。それから、C-x * r すると、データが取り込まれた状態でcalcが起動 してくる。
1: [ [ 18010405, 138, 76, 55 ] [ 18010421, 123, 68, 64 ] [ 18010505, 136, 70, 55 ] [ 18010521, 114, 60, 60 ] [ 18010606, 131, 71, 55 ] [ 18010621, 140, 68, 61 ] ]
このままでは、グラフを書くのに適さないので、v t で、行列を転置する。 次にv u で行列をベクトルに分解する。
4: [18010405, 18010421, 18010505, 18010521, 18010606, 18010621] 3: [138, 123, 136, 114, 131, 140] 2: [76, 68, 70, 60, 71, 68] 1: [55, 64, 55, 60, 55, 61]
後は、自由にグラフが描けるぞ。 高、低血圧、脈数と3つ揃っていれば、3次元グラフを g F で描ける。マウス でグリグリ回転させて、3者間の相関を調べるにうってつけ。
余り大量にデータを取り込めないようなんで、 その積もりでね。(上記なら、70行ぐらいはOKだった。)
etc of calc
k p prime? k n next prime k f factor k g gcd k l lcm s n store TOP (n: 0,1,...) r n restore TOP (n: 0,1,...) P pi (3.14159265359) 1 E e (2.71828182846)
parts of calc
こんな多機能な電卓がどれぐらいの規模か調べてみた。
cent:calc$ wc * : 3928 15983 141956 calc.el 3667 13409 125732 calcalg2.el 1929 7620 65583 calcalg3.el 1672 6818 56758 calccomp.el 301 1101 9351 calcsel2.el 56561 216744 1933882 total
がんばってますなあ。そろそろ飽きてきたので、河岸をかえてみるか。
gforth
debianに入れようとしたら、インストール時にエラー。何でかなと、ログを眺 めていたら、m4が入っていなくて、失敗してた。今迄、数々のものをインストー ルしてるんで、環境は十分に整っていると思っていたんだけど、古典的なもの が抜けていたのね。
M-x run-forth で、走り出すのがちょっと蹟だな。単語を調べるには、M-TAB すればいいのね。
チュートリアルに面白い機能が紹介されていた。逆アセンブルできるとな。
sakae@usvr:~$ gforth Gforth 0.7.3, Copyright (C) 1995-2008 Free Software Foundation, Inc. Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license' Type `bye' to exit see + Code +
やってみた。そして見事にハングしたよ。で、一日寝て考えた。いくらなんで も、ガチで逆アセを実装してる事はあるまい。きっと誰かに委託してるぞ。 予想確認の為、ハング中に別端末からpsしてみた。
2309 pts/2 S+ 0:00 gforth 2314 pts/2 S+ 0:00 sh -c type mktemp >/dev/null && type gdb >/dev/null 2318 pts/2 S+ 0:00 gdb -nx -q -p 2309 -x /tmp/gforthdis.N5VvdlqjLK
ビンゴ! gdbさんに頼んでいるのね。じっとコマンド列を眺めていて、テンポ ラリーファイルに鍵がありそうと思った。
sakae@usvr:~$ cat /tmp/gforthdis.N5VvdlqjLK set verbose off set logging file /tmp/gforthdis.F7CsJmaKB4 set logging on disas 0x558F5A6151A9,0x558F5A6151CF set logging off quit
やっぱりそうなのね。最近は生きづらい丗の中になったなあ。自分のプロセス にもアクセス出来ないのか。ならばsudo gforthではどうよ。
see + Code + 0x000055709f5f81a9 <gforth_engine+2873>: mov %r15,0x21b790(%rip) # 0x55709f813940 <saved_ip> 0x000055709f5f81b0 <gforth_engine+2880>: lea 0x8(%r14),%rax 0x000055709f5f81b4 <gforth_engine+2884>: mov (%r14),%rdx 0x000055709f5f81b7 <gforth_engine+2887>: add $0x8,%r15 0x000055709f5f81bb <gforth_engine+2891>: add %rdx,(%rax) 0x000055709f5f81be <gforth_engine+2894>: mov %rax,%r14 0x000055709f5f81c1 <gforth_engine+2897>: mov -0x8(%r15),%rcx 0x000055709f5f81c5 <gforth_engine+2901>: jmpq *%rcx 0x000055709f5f81c7 <gforth_engine+2903>: mov -0x8(%r15),%rcx 0x000055709f5f81cb <gforth_engine+2907>: jmpq *%rcx 0x000055709f5f81cd <gforth_engine+2909>: jmpq *%rcx end-code
ああ、これウブ上での結果ね。すこし融通が効きそうなやつでやってみると、
FreeBSD ok
see + Code + 0x0000000000407363 <gforth_engine+10915>: mov %r14,0x21abd6(%rip) # 0x621f40 <saved_ip> 0x000000000040736a <gforth_engine+10922>: lea 0x8(%r13),%rax 0x000000000040736e <gforth_engine+10926>: mov 0x0(%r13),%rdx 0x0000000000407372 <gforth_engine+10930>: add $0x8,%r14 0x0000000000407376 <gforth_engine+10934>: add %rdx,(%rax) 0x0000000000407379 <gforth_engine+10937>: mov %rax,%r13 0x000000000040737c <gforth_engine+10940>: mov -0x8(%r14),%rcx 0x0000000000407380 <gforth_engine+10944>: jmpq *%rcx end-code ok
あれれ、コードサイズが違うな。この差は、gcc vs. clang由来? gforth-fastてのが提供されてたので、そちらで試してみた。
see + Code + 0x0000565314e693d3 <gforth_engine+2403>: add $0x8,%rbp 0x0000565314e693d7 <gforth_engine+2407>: add $0x8,%r15 0x0000565314e693db <gforth_engine+2411>: add 0x0(%rbp),%r14 0x0000565314e693df <gforth_engine+2415>: mov -0x8(%r15),%rcx 0x0000565314e693e3 <gforth_engine+2419>: jmpq *%rcx 0x0000565314e693e5 <gforth_engine+2421>: mov -0x8(%r15),%rcx 0x0000565314e693e9 <gforth_engine+2425>: jmpq *%rcx 0x0000565314e693eb <gforth_engine+2427>: jmpq *%rcx end-code
make bench
gforthのINSTALLを見ていたら、ベンチマークが紹介されてた。
sakae@usvr:~/src/gforth-0.7.3/build$ make bench : time ./gforth-fast --die-on-signal -p ".:/usr/local/lib/gforth/site-forth:/usr/local/share/gforth/site-forth:/usr/local/lib/gforth/0.7.3:/usr/local/share/gforth/0.7.3:.." fib.fs -e "main bye" 0.18user 0.01system 0:00.20elapsed 99%CPU (0avgtext+0avgdata 2948maxresident)k 0inputs+0outputs (0major+264minor)pagefaults 0swaps
ちょいと醜いので整理して、色々なgforthで試してみる。
sakae@usvr:~/src/gforth-0.7.3/build$ time gforth fib.fs -e "main bye" real 0m0.294s user 0m0.293s sys 0m0.000s sakae@usvr:~/src/gforth-0.7.3/build$ time gforth-fast fib.fs -e "main bye" real 0m0.212s user 0m0.196s sys 0m0.017s sakae@usvr:~/src/gforth-0.7.3/build$ time gforth-itc fib.fs -e "main bye" real 0m0.314s user 0m0.295s sys 0m0.014s
何かを犠牲にしたり、きっちりと保護したりしてるのだろう。それより、試供 品のfib.fsがどんな風になってるか興味有り。
sakae@usvr:~/src/gforth-0.7.3/build$ cat ../fib.fs \ Note: This is incorrect ("n fib" produces the result for fib(n+1)), \ but we do not change it to ensure that future timing results are \ comparable to older timing results. : fib ( n1 -- n2 ) dup 2 < if drop 1 else dup 1- recurse swap 2 - recurse + then ;
これがソースだ。そして、それを逆アセしたもの。
see fib : fib dup 2 < IF drop 1 ELSE dup 1- fib swap 2 - fib + THEN ; ok
ちょいと変形させて、factにしてみる。
: fact ( n1 -- n2 ) dup 2 < if drop 1 else dup 1- recurse * then ; : main 5 fact . ;
定義中は、手続名を内部で使えないので、仮の名前recuseを使う訳ね。
1 1 = . -1 ok 1 2 = . 1 2 = . 0 ok true . true . -1 ok false . false . 0 ok
factなら、= を使え! trueが -1 で、falseが 0 なのか。
debug
gforth-itcを起動すると、debuggerを使えるようになる。factでやってみるか。
5 dbg fact : fact Scanning code... Nesting debugger ready! [ 1 ] 00005 7F3061E83318 559E43966498 dup -> [ 2 ] 00005 00005 7F3061E83320 559E439661C0 2 -> [ 3 ] 00005 00005 00002 7F3061E83330 559E43966320 < -> [ 2 ] 00005 00000 7F3061E83338 559E439660B8 IF -> [ 1 ] 00005 7F3061E83370 559E43966498 dup -> [ 2 ] 00005 00005 7F3061E83378 559E439661F8 1- -> [ 2 ] 00005 00004 7F3061E83380 559E43966070 fact -> [ 5 ] 00004 00003 00002 00001 7F3061E83390 559E43966218 * -> [ 4 ] 00005 00004 00003 00002 7F3061E83398 559E43966088 THEN ; -> ok .s <1> 120 ok
リターンキーを叩くだけじゃ、再帰の追跡はやってくれないのね。n コマンド で追うといいのかな。
Floating point stack
浮動小数点もサポートしてる。スタックに積むには、e形式にする。整数用と スタックが別になってる。
1.23e0 4.5678e0 f.s <2> 1.230000000000E0 4.567800000000E0 ok f* f. 5.618394 ok
浮動小数点スタックの操作は、接頭語としてfを前置することになってる。
1234.5678 .s <2> 12345678 0 ok 1234.5678e0 8.7654321e3 f.s <2> 1.234567800000E3 8.765432100000E3 ok f. 8765.4321 ok
間違って、e表記を忘れると、数値はデータスタックの方に入ってしまう。デー タ自身も小数点が省かれているし、更に余計な0も積まれている。
一方、e形式の数値は、正規化されて格納される。データを取り出すと、世 間一般並の形で表示される。
疑問が一つある。なぜe表記の数値のみを浮動小数点スタックに入れる設計に したのだろう? 数値のパースの段階で、小数点が付いていたら、それ用のス タックに入れるって事が十分に出来たはず。 うえの例のような間違いも無くなると思うんだけど。深遠な理由が思いつかな いぞ。
互換性の問題かとも思ったけど、オブジェクトオリエンテッドをサポートした 時点で、完全に破綻してるからなあ。
だれか納得のいく説明を求む。