factor(3)

こう熱いと毎日お世話になっている、霧ヶ峰。世の中にはもろに地名を冠した製品があって この霧ヶ峰もその一つ。涼しい風を送ってくる夏の救世主だ。これが無いと、まじで死ねる。

霧が峰が有るのなら、軽井沢って名前のクーラーが有っても良さそうだけど、あちらは某電鉄が 開発して有名になったんで、電機メーカーは利害の関係から、名前を頂くのを遠慮したの かしらん。

おいらも某電鉄には遠慮して、霧ヶ峰に足を延ばしてみた。小淵沢から、八ヶ岳って言う、 某さんのパラボラアンテナ詣でを彷彿させるようなルート。いろいろ牧場が有ったな。頭が黒い羊は、 イギリス原産の食べられちゃうやつ。頭が白いやつは羊毛採取用のニュージーランド原種 とか。初めてそんな事を知りましたよ。

エコーライン、ビーナスラインを経由して、白樺湖、車山スキー場を過ぎて登って行くと、 やがて富士見台。この地名も駅の名前に取り入れられるくらい有名だな。

富士山頂に設置されたLTE基地局のアンテナや、登山道渋滞(きっと信号機が設置されるに 違いない)は観測できなかったけど、八ヶ岳山麓のなだからなスロープの向こうに、秀峰富士が くっきりと見えましたよ。

富士見台の回りは、ニッコウキスゲの見ごろ。オレンジ色に囲まれるのもいいもんだな。 このニッコウキスゲって、百合に似てるんだけど、同族なんですかね?後で調べてみるかな。

遊歩道を歩いて、火照った体を過ぎて行く風は、本物の霧ヶ峰の風でしたよ。三菱さんには 悪いけど、絶対に再現出来ないと思うよ。

ああ、風で思い出した。数年前から話題をさらってる、羽の無い扇風機。本体内に、ジェットエンジン に採用されてる、エアータービンが入っているそうな。その圧縮機で作った風を、丸いリング の回りに開けた小さい穴から噴出するそうだ。

そうすると回りの空気がそれに引きずられて、動くとか。風の流れを増幅する効果が有る んで、エアーマルチプライヤーとか言うそうだ。よく考えるね。欲しいけど、高いもんな。 霧ヶ峰で、我慢、我慢。

factorでスクリプト

今年も盛大にLL祭りが行われるようだ。開催場所が下町ってのがいいよね。何でもいいから 法被を着て捻り鉢巻で行くと、特別プレゼントが有ったりして!

このLL祭りにもきっと取り上げられる事は無いであろうfactorで、一人祭りを開いてますよ。 だって、結構大飯喰らいなんだもん! LLとは呼べないよね。

  PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND
 3055 sakae     20   0  5864 3208 1704 S   0.0  0.6   0:00.24 bash
 3247 sakae     20   0  215m  80m 3240 R   0.0 16.1   0:01.86 factor

大きい事は、Java並みさ。どうだ凄いだろーー。

で、前回やった、階乗を求めるのを、豪快にスクリプト化してみます。アプリにしちゃうと 小さくなっちゃってつまんないでしょ。

sakae@debian7:~/factor/work/fact$ cat fact.factor
#! /home/sakae/factor/factor

USING: kernel math command-line sequences math.parser prettyprint ;
IN: fact

: fact ( n -- n! )
         dup 0 =
         [ drop 1 ]
         [ dup 1 - fact * ]
         if
;

! : debug-me ( -- ) { "10" } first string>number fact . ;
: run-me ( -- ) (command-line) second string>number fact . ;
MAIN: run-me

下記が実行例。巨大数字をいとも簡単に使える所は、Javaと違いますな。失礼しました。

sakae@debian7:~/factor/work/fact$ ./fact.factor 100
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

(command-line)という、おいらの感覚からすれば、ちとトチ狂ったワードの実行で、スタックに 引数の配列が載ってきます。一番目がスクリプト名、二番目が目当ての文字列。

二番目を取り出して、文字列から数字に変換しfactに喰わせ、最後は、目立たないけど、ドット で、スタックのTOPを綺麗に表示って訳。ワード毎にボキャブをUSINGするのが、面倒ちゃ 面倒。だから、LLっぽくないね。でも、データがさーと流れて行って、加工されてく様は LLぽいか。 一体、どっちねん?

巨大な中身

上で、factorは大飯喰らいだって、外から観察出来たけど、内部情報をリークする仕掛けも 用意されてるのね。これなら、いくらリークしたって、美国からの圧力は受けませんから 安心です。で、どうなってるかと言うと

IN: scratchpad room.
== Data heap ==

- Nursery space
Size:     1,024 KB
Occupied: 533 KB
Free:     490 KB

- Aging space
Size:     2,048 KB
Occupied: 819 KB
Free:     1,228 KB

- Tenured space
Size:             98,304 KB
Occupied:         66,553 KB
Total free:       31,750 KB
Contiguous free:  31,741 KB
Free block count: 123

- Miscellaneous buffers
Card array: 405 KB
Deck array: 0 KB
Mark stack: 256 KB

== Code heap ==

Size:             65,532 KB
Occupied:         12,548 KB
Total free:       52,983 KB
Contiguous free:  52,979 KB
Free block count: 47

Optimized code:   48777 blocks 9,123 KB
Unoptimized code: 28347 blocks 2,534 KB
Inline caches:    0 blocks     0 KB
Profiling stubs:  6563 blocks  890 KB

roomって、かのLispにも有ったな。と言う事はgcも有るんかな?

Primitives
Word           Stack effect
 all-instances ( -- array )
 compact-gc    ( -- )
 gc            ( -- )
 minor-gc      ( -- )
 size          ( obj -- n )

Ordinary words
Word                 Stack effect
 instances           ( quot -- seq )
 save                ( -- )
 save-image          ( path -- )
 save-image-and-exit ( path -- )
 saving-path         ( path -- saving-path path )

嗚呼、やっぱりそっくりだ。違いは、前置法か後置法の違いしか無かったりして。

dc

この巨大なイメージをコントロールしてるVMはどうなってる? 現場へ行ってみたら、 Cふらふら語で書いてあった。includeしてるHeaderファイルも山のように有るし、何処から 手(目)を付けていいのやら? 潔く撤退しましょ。

Cふらふらっておいらが即興で付けた、C++ の別名です。目がくらくらして、(おいらの) 体がふらふらしちゃうからです。特段C++を攻撃しようって意図はありませんです。

そう言えば、昔々、forthもどきでPRNのカリュキレータを某装置用に作った記憶が有るな。 某装置は独自OSと言うか言語体系で動いていたんで、電卓の類は入っていないんだ。

現場で計算が必要になった時、電卓を所持してなくて困ったので、即興で作ったよ。 数字だったらスタックに積む、演算子(+-*/%)が出てきたら、値をポップして計算し、 それをまたスタックに積む。= が来たら、ポップして表示。

折角作ったんで、8インチフロッピーに仕舞って持ち帰り。使えそうなんで、残業して ちょっと機能拡張。数字とかの入力は、2進、8進、16進も受け付けるようにし、結果表示も 進数を指定出来るようにした。近隣に配ってあげたら、喜ばれたよ。

後世になってFreeBSDに触れてみたら、同じような機能の電卓が有るのね。その名は、

NAME
     dc - desk calculator

その効用は

解説
       dc は、逆ポーランド形式の無限精度の計算が行える卓上計算機です。この電卓
       は、定義やマクロ呼び出しも行えます。普通、dc は標準入力から読み込みます
       。 コマンドライン引数が与えられた時は、それはファイル名となり、 dc はそ
       のファイルを読み込み、ファイルの内容を実行した後で、標準入力から入力 を
       取 ります。通常の出力はすべて標準出力へ、エラー出力はすべて標準エラー出
       力へ送られます。

       逆ポーランド記法計算機は、数をスタックに保存します。数字を入力すると 、
       そ れをスタックに積み上げます。計算操作は、引数をスタックから取り出し、
       結果をスタックに積み上げます。

       数字を dc に入力するためには、数字 (小数点が有っても構いません) を入 力
       し ま す 。指数表現はサポートされていません。負の数字を入力するためには
       、``_'' で始まる数字を入力します。 ``-'' は減算の二項演算子として使われ
       ているので、このために利用することはできません。引き続いて 2 つの数字を
       入力するためには、あいだに空白文字か改行文字を入力します。これらは、 コ
       マンドとしての意味はありません。

つらつらと解説を読んで行くと、機能限定のforthって事が分かります。色々な進数も 扱えるし、マクロも組めるし、ソース嫁にうってつけ。

ficl

後身近に有ったforthとして、sparcのmonitorがそうだったな。何? sparcなんて知らないよ ってんなら、FreeBSDはどうだ。

FreeBSDのboot loaderには、forthが組み込まれている。

[sakae@pcbsd /boot]$ ls *.4th
beastie.4th             frames.4th              shortcuts.4th
brand.4th               loader.4th              support.4th
check-password.4th      menu-commands.4th       version.4th
color.4th               menu.4th
delay.4th               screen.4th

沢山有るんで、どれか一つ、support.4thでも開いてみると

\ $FreeBSD: releng/9.1/sys/boot/forth/support.4th 222417 2011-05-28 08:50:38Z julian $

\ Loader.rc support functions:
\
\ initialize ( addr len -- )    as above, plus load_conf_files
\ load_conf ( addr len -- )     load conf file given
\ include_conf_files ( -- )     load all conf files in load_conf_files
\ print_syntax_error ( -- )     print line and marker of where a syntax
\                               error was detected
\ print_line ( -- )             print last line processed
\ load_kernel ( -- )            load kernel
\ load_modules ( -- )           load modules flagged

でもって、4thのコードが有るなら、それを動かす本体が有るはず。探してみると

[sakae@pcbsd /usr/src/sys/boot/ficl]$ ls
Makefile        ficl.h          loader.c        prefix.c        testmain.c
amd64           fileaccess.c    math64.c        search.c        tools.c
arm             float.c         math64.h        softwords       unix.c
dict.c          i386            mips            sparc64         vm.c
ficl.c          ia64            powerpc         stack.c         words.c

いろいろなマシンで動くようだね。面白い、実に面白い って、ガリレオ風に言って おこう。

FreeBSDにご縁の無い方は、 ficlって所を訪ねてみれば、きっと会えるよ。

on LOL

上の4thにしろ、Cで書かれていて、ちょっとソファーでくつろぎながら見るには適さない と思う。もっと他に無いか? ずっと考えていたら、昔買った本にforthの実装が 載ってたような。。。

かたっぱしから蔵書を開いてみましたよ。こう書くと、沢山本が有ると思われるかも 知れないけど、そんなにないから、ものの1分で検索終了。

LOL本こと、LET OVER LAMBDAに、Lisp(のマクロを 駆使)しで書いた forthの実装が載ってるんよ。lisp語で書いてあると読むのも理解するのも簡単。いいね。

コードも出てたので、動かしてみるか。 LISPはいいやってんで、入れてなかったけど、これを期に鉄鋼銀行共通LISPでも入れておくかな。

Stack machine

forthの基礎になっているのは、スタック。 スタックを駆使して動いている言語は 非常に多い。RubyしかりJavaしかり。 そして、言語設計者が勝手気ままに、仮想スタックマシンを設計、製作してる。

勝手気ままにと言っても、それなりのセオリーは有るはず。探してみたら Stack Computers: the new wave なんてのが見つかったよ。面白そうだから、眺めて見れ。 return stackなんて記事も、 参考になるぞ。forth一般については、FORTH を使うための手引きが お勧めです。

factor入門から

いつの間にか、factorから離れてしまったので、軌道修正します。 forthの一方言であるfactorは、なかなか秘語めいています。回りで喋ってくれる人も 余りいないため、真髄を極めることがむずかしい。こういう時は、先人に学ぶのが鉄則。

factor入門が お勧めです。

この入門を読んでいて、面白いと思った所をピンポイントで使ってみます。

factの中にも出てきた。鍵括弧でくくるやりかた。lambda だよって事でした。lambdaって 何だって事ならなんでもλですね。

処理ブロックとしての使い方になるんですかね。後は遅延処理の為ですかね。

( scratchpad ) [ 1 3 + ]

--- Data stack:
[ 1 3 + ]
( scratchpad ) call

--- Data stack:
4

処理を定義して(結果は、お約束通りstack上に残る)、それを後で呼び出す。これが まあ普通の使い方。

処理を定義する時、マクロっぽく、値を埋め込めるとな。

( scratchpad ) USE: fry
( scratchpad ) 4 '[ 1 _ + ]

--- Data stack:
[ 1 4 + ]

次は、dipを使った例。割り算の商と余りを求めるのに /mod ってのがあるけど、例題だから 手動でやってみる。2つのデータを受け取り、演算後 商と余りをstackに残すってやつ。

( scratchpad ) 20 3 2dup

--- Data stack:
20
3
20
3
( scratchpad ) [ /i ] 2dip

--- Data stack:
6
20
3
( scratchpad ) mod

--- Data stack:
6
2

dipは、TOPにある関数の実行に先立ち、その下側にある値を保護する機能がある。また、/i は、 整数の商を求めるワードだ。

( scratchpad ) 20 3 2dup /i -rot mod

--- Data stack:
6
2

別解として、-rotを使ったのも。-rotってのは、火山のイメージ。TOPを含めて3つのデータに ついての演算。下からデータが上に湧き上がってくる。元々TOPに有ったデータは、底に潜るって訳。 地球のマグマの流動を表しているんだ。(何と壮大な事)rotは、逆に沈み込むやつね。沈んで 空いた所には、3番目に有ったやつが出てくる。forthは、大体3個までのデータを相手に した演算が多いよ。dupは、TOPをコピーするんだけど、2dupになると、TOPから2個分を コピーするって具合。

ROTの場合、どちらの方向へ回転するんだったけな? ってのをよく間違えるんだけど、上の コメントを書いていて、連想が沸いてきたよ。湧き上がってきて噴火するのは、富士山噴火 みたいに、マイナスだよねーって覚えればいいんだ。所で、噴火して喜ぶ人っているの?

いえね、最近、『なぜタクシーは動かなくてもメーターが上がるのか』なんて本を読んだんだ。 その本には、一面の見方だけじゃ危険だよって書いてあったんでね。

更に、頑張って、別解。使う道具は

( scratchpad ) \ 2bi see
IN: kernel
: 2bi ( x y p q -- ) [ 2keep ] dip call ; inline

xとyに対して、pを適用、更にxとyに対してqを適用ってやつです。

( scratchpad ) 20 3 [ /i ] [ mod ] 2bi

--- Data stack:
6
2

雅にジャスト・フィットして雅に書けました。まあ、縁の下では、上に上げたようなルーチン が動いているんですけどね。

ああ、keepとかが使われているんか。で、その定義と例は?

( scratchpad ) \ 2keep see
IN: kernel
: 2keep ( x y quot -- x y ) [ 2dup ] dip 2dip ; inline
( scratchpad ) 20 3 [ /i ] 2keep

--- Data stack:
6
20
3

いいね。ソースも例もすぐに引けるしね。入門を丁寧に追って行くときりがないので、 後一つだけ、華麗に行こう。

( scratchpad ) \ curry see
IN: kernel
TUPLE: curry { obj read-only } { quot read-only } ;

USING: classes.tuple.private quotations slots ;
IN: kernel
: curry ( obj quot -- curry )
    dup callable? [ \ callable bad-slot-value ] unless
    { curry 2 1 tuple 57438726 curry -49469977 } <tuple-boa> ;
    flushable
( scratchpad ) 3 [ + ] curry

--- Data stack:
[ 3 + ]

なるほど、埋め込まれたな。使い道を考えないと華麗に使いこないせないな。