Common Lisp の勉強のために読んだ LET OVER LAMBDA Edition 1.0 の中で、スタック指向の言語 FORTH に出会った(この本は素晴らしい内容にも関わらずパタッと本屋さんで見掛けなくなった。Amazon にも定価オーバーの出品しか無い。Lisp、しかも Common Lisp を本気で使いたいと思う人には一読の価値がある良書だと思うので、定価販売されているものをもし見つけたら即買することをおすすめする。あ、でも英語が読めるなら著者のダグが http://letoverlambda.com/ で太っ腹に公開してくれてるので、まずはこちらを覗いてみよう)。
そして、FORTH で 逆ポーランド記法 に出会った。
逆ポーランド記法
1 と 3 を足した結果を 2 で割る、という計算を書く時、算数で習った数式の記述ならこう書く。
(1 + 3) / 2
Perl や JavaScript など C言語系のプログラミングでも同じだ。
ex: JavaScript
var foo = (1 + 3) / 2;
これを 中置記法 という。
Lisp など、前置記法(ポーランド記法) を採用している言語ではこうなる。
ex: Common Lisp
(/ (+ 1 3) 2)
Lisp では「+
」や「/
」も通常の関数と同じように扱われる。
C言語系の言語でも + を add、/ を div という風に関数化した場合同様に前置記法になる。
ex: Perl
sub add { my ($x, $y) = @_; my $ret = $x + $y; return $ret; } sub div { my ($x, $y) = @_; die "Oops! Division by zero." if $y == 0; my $ret = $x / $y; return $ret; } my $foo = div(add(1, 3), 2);
Lisp は一部の特殊オペレータを除いて、リストの最初のシンボルが関数として適用されるという非常にシンプルなルールが言語全体に行き渡っているから、計算の曖昧性がなく、抽象化もし易い構造になっている。
Lisp などで採用されている前置記法の逆を行くのが、FORTH や本記事の主題である Factor で採用されている逆ポーランド記法だ。
ex: Factor
1 3 + 2 /
FORTH や Factor では、リテラルや計算結果をスタックに積んだり、逆に取り出したりしながら処理を進める。
1 3 + 2 /
という計算は、次のような仕組みになってる。
- スタックに 1 を push (積む)
- スタックに 3 を push
- スタックに積まれた 1 と 3 を pop (取り出)して、足し合わせた(+)結果(4)を push
- スタックに 2 を push
- スタックから 4 と 2 を pop して、前者を後者で割った(/)結果(2)を push
最初はなんじゃそりゃ?と思うかもしれないし、何が良いのか疑問に思う人もいるかもしれない。
スタックと逆ポーランド記法を使ったこのプログラム構造の利点については(僕自身がまだ初心者だというのもあるし)とても奥が深いテーマなので詳しい議論は避ける。
が、プログラマなら、この簡単な例だけでも他の言語との大きな違いを幾つか見付けられるだろう。
パッと目に着くのは、逆ポーランド記法では計算の優先順位を操作するための括弧が必要無いということだ。
もう一つ。
他の言語では計算結果の一時避難場所として foo のような変数を設けていた。
でもスタック型言語では常にスタックと共に処理が進んでいくから、余計な変数を宣言する必要がない。
FORTH から Factor を見付ける
Lisp の前置記法も学のない僕にとっては眼から鱗だったけど、FORTH のスタックと逆ポーランド記法を組み合わせて、且つ、Word 単位で抽象化していく手法も驚きであった。
しかしながら、FORTH の処理系(gforth を試した)は浮動小数点の扱いなどが今一飲み込めず、「実用」という点ではなかなか手が出ず、日曜プログラミングの領域で遊んでみる程度に留まっていた。
僕はフリーランスでプログラマをやる傍ら、仕事とはちょっと離れたところで普段は使わない言語を学ぶのが好きだ(ちなみに業務用には Common Lisp, Perl, Shell Script そして何はともあれ SQL を主に使っている)。
どうせ実務以外で勉強するなら、脳細胞を活性化してくれるような、エレガントで少数派の言語がいい。
実際、Lisp も最初は趣味だったが、やればやるほど魅せられてしまい、1年という時間を掛けて(元々使っていた Perl を乗り越えて)実務上のメイン言語になった。
僕は学術研究や最先端の技術開発をしているわけではないので、FORTH の哲学やエレガントさには惹かれても、やっぱりどこかで常に「日常的な業務で使えるかどうか」を考えてしまう。
ある意味ではそんなどーでもいい悩みなんだけど、ちょっと FORTH に対して悶々としたものを抱えながらなんとなく Web を検索したところ、本記事の主役 Factor に出会った。
Factor は FORTH で僕が躓いてしまった浮動小数点の処理などが既に組み込みで他の型と同様に使えることはもちろん、FORTH のエレガントなスタイルはそのままに Lisp 的な機能(例えば map などの高階関数)をサポートしていたり、世の便利なツールを取り込むためのライブラリも普通に充実している。
最小限のチュートリアルという感じではあるが、日本語のドキュメントもある。
Factor のスタックを利用した連鎖性言語としての特徴についてはここにまとめて解説が載ってる。初めてこのような概念に触れるととても興味深い。
何はともあれ Terminal で REPL
Factor のインストール方法については 本家サイト に載ってるままなので省略する。
が、僕個人としては標準のターミナル(ちなみに OS は Mac OS X 10.9)から REPL で操作するほうが好きなので、その場合の小技を少し書いて終わりたい。
まず、Factor を dmg ファイルからアプリケーションフォルダにインストールしても、それだけだとターミナルから factor というコマンドで起動できるわけではない。
Factor をターミナルで起動するには、アプリケーションに同梱されているエイリアスを叩く。
標準的なインストールなら
/Applications/factor/factor
がそれだ。$ /Applications/factor/factor IN: scratchpad
ただこれ、かなり不格好だ。
そして大事な点をもう一つ。
factor.app の方もそうなんだけど、上下矢印キーで実行したコマンド履歴を行ったり来たりできない。
なので、/Applications/factor/factor
を rlwrap の引数として呼び出すコマンドを factor という名前の alias にアサインした。
まず rlwrap は Mac OS X に元々入っていないと思うので、MacPorts なら、
$ sudo port install rlwrap
でインストールできる。
その上で ~/.bash_profile
などシェルが参照している環境設定ファイルを開き、次の行を書き加えればOK。
alias factor="rlwrap /Applications/factor/factor"
これで $ factor
というコマンド入力で履歴表示付き REPL が使えるようになる。
ちなみに REPL を終了させるときは ^D (Control + D)で終了させている(多分、他に方法がありそうだけど、まだ見つけてない・・・)。
Factor 勉強中メモは別サイトにまとめている。
basicwerk.com/memoize/