ちょっとした換算に便利なFrink

 Linuxには自由に使える電卓や単位換算ツールが数えきれないほどあるが、特に興味深いのが Frink だ。このツールのねらいは、単位の使い方をチェックしたり、細かい数値を気にせずにちょっとした換算や実世界の計算を手早くこなせるようにすることにある。

 openSUSE 11のユーザはFrinkを1-Clickでインストールできるが、Ubuntu IntrepidやFedora 9のリポジトリにはFrinkのパッケージが見当たらない。そこで今回は、パッケージではなくfrink.jarファイルを利用する。frink.jarファイルにはJavaのクラスファイルが含まれており、Java Runtime Environment(JRE)がインストールされているマシンなら、直接このファイルからFrinkを実行できる。

 Frinkはコンソールアプリケーションとして動作するほか、AWTとSwingのどちらのJavaツールキットで作成されたGUIでも実行できる。GUIツールの大きな利点は、1つ前の計算式を表示する上矢印キーが使えることだ。コンソール版Frinkはそのままでは上矢印キーに対応していないが、これについてはrlwrap経由で実行するという対処法がある。readlineラッパーであるrlwrapを利用すれば、コンソール版Frinkでも上矢印キーによる履歴の表示など、多くのキー操作が可能になる。rlwrapのパッケージは、Ubuntu IntrepidおよびFedora 9の標準のリポジトリに用意されているほか、openSUSE 11の1-Click対応のものも存在する。Frinkのグラフィックプログラミング機能を使わないなら、次のようにターミナルからrlwrap経由でFrinkを実行することをお勧めする。

$ cat ~/bin/frink
rlwrap java -cp /FromWeb/frink.jar frink.parser.Frink "$@"

$ ~/bin/frink
Frink - Copyright 2000-2008 Alan Eliasen, eliasen@mindspring.com.
...

 FrinkのWebサイトには、Windows用バイナリはあるがLinuxのものはない。同サイトには、gcjによるネイティブ実行ファイルのコンパイルには1時間以上かかると記されているが、私の場合は、もっと短時間で済んだ。バイナリからデバッグ情報を取り除くと、実行ファイルのサイズは2.5MBほどになる。先ほど説明したjarファイルをOpenJDK Runtime Environment(build 1.6.0-b09)で実行した場合に比べると、gcj(4.3.0 20080428)でコンパイルしたバージョンはかなり起動時間が短くなった。同じように起動と簡単な計算を行うのにかかった時間は、それぞれ1.8秒、0.4秒だった。

$ gcj -O3 -fomit-frame-pointer --main=frink.parser.Frink -o frinkx.exe frink.jar
$ ls -lh frinkx.exe
-rwxrwxr-x 1 ben ben 3.4M 2008-11-04 11:52 frinkx.exe*
$ strip frinkx.exe
$ ls -lh frinkx.exe
-rwxrwxr-x 1 ben ben 2.5M 2008-11-04 11:55 frinkx.exe
$ ldd frinkx.exe
	linux-vdso.so.1 =>  (0x00007fff652e3000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003a52600000)
	libgcj.so.9 => /usr/lib64/libgcj.so.9 (0x00000035fcc00000)
	libm.so.6 => /lib64/libm.so.6 (0x000000000087a000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x0000000000aff000)
	librt.so.1 => /lib64/librt.so.1 (0x0000000000d1a000)
	libz.so.1 => /lib64/libz.so.1 (0x0000003a50600000)
	libdl.so.2 => /lib64/libdl.so.2 (0x0000000000f23000)
	libc.so.6 => /lib64/libc.so.6 (0x00000000059b6000)
	/lib64/ld-linux-x86-64.so.2 (0x0000000000110000)

 Frinkのコマンドライン引数では、各種オプションのあとに、読み込みと実行の対象となるFrinkコードが記述されたファイル名を指定する。ただし「-e」オプションを使えば、ファイル名の代わりに、実行すべき式を最後の引数として与えることができる。Frinkの1回の呼び出しで複数のファイルを実行する場合は、「-f filename1 -f filename2 ... 」のようにすればよい。「-k」オプションは、指定のファイルを実行したあともFrinkを入力待ち状態にしておくためのもので、「-f」オプションと組み合わせて、カスタムの変数および関数を起動ファイルから読み込んだうえで計算を実行する場合に役立つ。

 では、Frinkによる計算はどのように行われるのだろうか。ここで、ノートPC用バッテリ容量の表示単位がメーカーによってアンペア時とワット時で異なっている場合を想定する。これら2つの単位を揃えるには、次のようにすればよい。なお、Frinkでは「frink>」のようなプロンプトが出ないので、以下では入力行を太字で示す。

6.6 amp hours * 16 volts -> watt hours
105.6

6.6 amp hours * 16 volts
380160.0 m^2 s^-2 kg (energy)
105.6 watt hours
380160.0 m^2 s^-2 kg (energy)

6.6 amp hours * 16 volts -> watts
 Conformance error
   Left side is: 380160.0 m^2 s^-2 kg (energy)
  Right side is: 1 m^2 s^-3 kg (power)
     Suggestion: multiply left side by frequency
              or divide left side by time

 最初の計算では、こちらで単位を指定したので、結果の105.6という値には単位が表示されていない。換算後の単位を明示的に指定した場合でも計算結果に単位を表示させるには、指定する単位を二重引用符で囲めばよい。続く2つの計算では、基本単位のエネルギーで結果が表示されている。Frinkでは、こうした国際単位系(SI)の使用がデフォルトになっているためだ(ちなみに、この単位系の通貨単位は米ドル)。デフォルトの表示単位を変更する方法は、後述する。また、見当違いな換算を試みた最後の行では、その修正方法がFrinkによって提示されているのがわかる。

 Frinkでは、浮動小数点数、有理数、さらには区間というデータ型まで使って、計算を行うことができる。また、通常の電卓にあるような算術演算子だけでなく、単位操作用の特別な演算子も用意されている。たとえば、conforms演算子は2つのオペランドが同じ次元に属する場合に真(true)を返す。また、「square」(sq、2乗)と「cubic」(cu、3乗)というキーワードは想像どおりの意味を持ち、「3 square feet」(3平方フィート)は「3 feet^2」と同じである。

 Frinkは、変数、if/then/else構文、ループ処理、配列、ディクショナリ(連想配列)、集合、関数をサポートしている。関数では、パラメータにデフォルト値を持たせたり、パラメータ値を特定の単位に制限したりできる。

 単位の追跡機能にはそれなりの効果があり、変数に適切な単位が与えられていなかったり等式に不備があったりするとそうした点を指摘してくれる。この機能の働きを確認するために、静止していた物体を加速度aで距離sだけ移動させると速度が2*a*sの平方根になることを示す運動方程式を考えてみよう。最初の例を見ると、もともとFrinkで値が定義されている重力(gravity)は正しい値と単位になっているが、移動距離sには単位が与えられていない。そのため、おかしなことに、本来はエネルギーとして表示されるはずの結果が加速度(acceleration)として表示されている。単位が間違っているので、平方根をとっても当然、求めたい速度は得られない。

gravity
196133/20000 (exactly 9.80665) m s^-2 (acceleration)

s=300
300
(2*a*s)
5880.0 m s^-2 (acceleration)
(2*a*s)^(1/2)
76.68115805072325 m^(1/2) s^-1 (unknown unit type)

 移動距離sをきちんとメートル単位(meters)として与えれば(以下を参照)、すべてが期待どおりにうまくいく。別の単位(yard)で移動距離を入力しても、やはり正しく計算が行われることがわかる。

s=300 meters
300 m (length)
(2*a*s)
5880.0 m^2 s^-2 (specific_energy)
(2*a*s)^(1/2)
76.68115805072325 m s^-1 (velocity)

s=5yards
1143/250 (exactly 4.572) m (length)
(2*a*s)
89.6112 m^2 s^-2 (specific_energy)
(2*a*s)^(1/2)
9.466319242451101 m s^-1 (velocity)

 無論、地球の重力はどこでも同じというわけではない。考慮すべき重力の範囲を表すのに区間を使うと計算が難しくなりそうだが、実はそうでもない。次の例の最初の行では、変数gintを重力の区間として宣言しているが、このgintを重力(gravity)の代わりに使う点以外は、先ほどの式とまったく同じで構わないのだ。数値の丸めが必要になった場合は、結果として得られる区間にとり得る値の全範囲が必ず含まれるように、下限については端数の切り捨て、上限については切り上げが行われる。また、区間どうしが重複する場合などは、明示的な区間比較の演算子を使って、あいまい性を解消したほうがよいだろう。

 
gint = new interval [ 9.797645, 9.80665 ] meters / (second^2) [9.797645, 9.80665] m s^-2 (acceleration) d=500feet 762/5 (exactly 152.4) m (length) (2*gint*s) [89.58966588, 89.6720076] m^2 s^-2 (specific_energy) (2*gint*s)^(1/2) [9.4651817668759, 9.4695304846651] m s^-1 (velocity)

 Frinkには、よく使われる三角演算、丸め、基数変換のほか、乱数の取得や対数/指数の計算のための関数や合同算術の実行に役立つ関数など、組み込み関数がひと通り用意されている。また、整数論に関する計算や文字列操作のための関数も存在する。

 デフォルトでは国際単位系になっているFrinkの表示単位は、「dimension :-> measure」という構文で変更できる。以下のように、Fahrenheit(ファーレンハイト)関数を使って得られた結果も、デフォルトでは絶対温度(K:ケルビン)で表示される。また、かっこ内には次元が示されている。真ん中のコマンドでは、温度(temperature)に対するデフォルトの単位をCelsius(セルシウス温度)に変更している。最後のコマンドでは、Fahrenheit関数の代わりにFrinkで定められた短縮形の“F”を使っているが、その結果は、明示的な変換を施さなくても、直前のコマンドで指定した単位、セルシウス度で表示されているのがわかる。なお、デフォルトで利用可能な関数については、frink.jarファイル内のunits.txtファイルを参照してほしい。

Fahrenheit[98.6]
310.15 K (temperature)

temperature :-> Celsius

F[98.6]
37.0

 Frinkコマンドが記述されたファイルを読み取る「-f」オプションと、ファイルの実行後も入力を受け付けるための「-k」オプションを使ってFrinkを呼び出せば、結果表示の単位を容易にカスタマイズできる。「-k」オプションが必要なのは、「-f」オプションで読み込んだ設定ファイルでは「dimension :-> unit」によるデフォルト単位の設定しか行わず、そのあとはコンソールから計算式を与えられるようにFrinkを入力待ちの状態にしておくためである。

 なお、Frinkの機能には、最新の情報を得るためにインターネット接続を必要とするものがある(通貨換算や言語変換など)。その場合は、こうしたサービスで利用するWebプロキシ情報を設定する必要があるだろう。

まとめ

 GUIの選択肢を数多く持ち、それ自体もJavaで書かれているFrinkは、処理能力の限られたモバイルデバイスからマルチコアCPUの高性能デスクトップマシンまで、さまざまなプラットフォームで実行できる。

 よく使う計算を再利用可能な関数にするための言語が用意されているというだけでも、Frinkはすばらしい簡易計算ツールだ。より複雑なアプリケーションをFrinkに追加するにはPythonやPerlが必要かもしれないが、Frink上でループ構文やif/else構文でユーザの入力や正規表現が利用できることを知っておいても損はないだろう。

Ben Martinは10年以上前からファイルシステムに携わっている。博士号を持ち、現在はlibferris、各種ファイルシステム、検索ソリューションを中心としたコンサルティング業務に従事。

Linux.com 原文(2008年11月14日)