前置き
- FileMaker Pro で Fizzbuzz問題 (参照:wikipedia) やってみようかなと思いました。唐突に。
- ついでに複数パターンでパフォーマンス測定もやってみます。
- 同じようなことやってる人いないかと思って検索してみたら、以下が見つかりましたのでご紹介まで。
FileMakerPro とは
- Apple の子会社である FileMaker 社が開発販売しているデータベースソフトのクライアント側。
- スクリプトと呼ばれる処理の中で FizzBuzz 問題を解ける。
- やろうと思えば、Let と Evaluate という関数を用いて、計算式単体でもできる。今回やってみる。
- 参照: 公式サイト - FileMakerPro
FizzBuzz とは
- ルール
- 1 から 100 までの自然数を列挙していく。
- 3 の倍数では Fizz を出力する。
- 5 の倍数では Buzz を出力する。
- 3 と 5 の公倍数では FizzBuzz を出力する。
- 参照: Wikipedia - FizzBuzz
下準備と条件
基本骨子
基本はスクリプトでやるので、まず、100回ループして改行区切りで 1~100 までの数字を書き出します。以下のような感じで。
条件
- フィールドは結果を出力するための一つのみ使用する。
- フィールドに値をセットするのは最後に一回だけ。
パターンA:Loop
やることは以下の通りです。
- 1~100 までの数字を出力する
- その途中で、以下の条件の際には出力する値を変える
- 3の倍数であれば:Fizz
- 5の倍数であれば:Buzz
- 3と5の公倍数であれば:FizzBuzz
下準備の基本骨子から、 $result に挿入していく値を $i から $value へ変更します。
$value の計算式は以下のように。
Case (
not Mod ( $i ; 15 ); "FizzBuzz";
not Mod ( $i ; 5 ); "Buzz";
not Mod ( $i ; 3 ); "Fizz";
$i
)
これで実行すると……
できました。
FileMaker の Case について補足しておくと、上から順に評価されるもので、最初に評価されたところで抜けます。
あまりに簡単で、これだけで終わってしまうとつまらないので、別パターンも考えてみます。
パターンB:Let + Evaluate 関数による再帰
やることは以下の通りです。
- 1~100 までの数字を出力する
- その途中で、以下の条件の際には出力する値を変える
- 3の倍数であれば:Fizz
- 5の倍数であれば:Buzz
- 3と5の公倍数であれば:FizzBuzz
パターンAと同じ。
ただし、スクリプトで Loop をまわすのではなく、一つの計算式内で Let + Evaluate 関数による再帰をおこないます。
まずは単純に、改行区切りで 1~100 までの数字が出力されるようにしましょう。計算式は以下のように。
Let (
[
$i = 1;
$max = 100;
$result = $i;
$formula = "
If (
$i ≠ $max;
Let (
[
$i = $i + 1;
$result = $result & Char(10) & $i
];
Evaluate ( $formula )
);
$result
)
"
];
Evaluate ( $formula )
)
パターンBにしていきなり下準備の基本骨子を使わないやり方になってしまったという……(基本骨子は次の段でもう一度出てきます!)
結果は以下の通り、ちゃんと出力されてくれます。
では次に、 Fizz Buzz FizzBuzz への対応を入れましょう。
Let (
[
$i = 1;
$max = 100;
$result = $i;
$formula = "
If (
$i ≠ $max;
Let (
[
$i = $i + 1;
$value = Case (
not Mod ( $i ; 15 ); \"FizzBuzz\";
not Mod ( $i ; 5 ); \"Buzz\";
not Mod ( $i ; 3 ); \"Fizz\";
$i
);
$result = $result & Char(10) & $value
];
Evaluate ( $formula )
);
$result
)
"
];
Evaluate ( $formula )
)
結果は以下。
基本ロジックはほとんどパターンAと同じなので、面白味が薄いかもしれませんが……
改行記号の失敗例
ちなみに $formula 定義内の $result の定義について以下のようにしてしまうと、
$result = $result & \"¶\" & $i
これの出力結果は、
となります。残念ながら改行記号が半角スペースに解釈されてしまうのですね。なので、最初の成功例で出したように Char(10) を指定してあげる必要があります。
爆死パターン:Substitute
やることは以下の通りです。
- 1~100 までの数字を出力する
- 出力し終えた後で、以下の条件に当たる行の値を置換する
- 3の倍数であれば:Fizz
- 5の倍数であれば:Buzz
- 3と5の公倍数であれば:FizzBuzz
なので、まず、下準備の基本骨子をそのまま使います。
ループが終わった後で Substitute の処理を噛ませます。そのためには Substitute 条件をループ中に出力してあげなくてはなりません。例えば $substitute という変数を置いて、以下のように。
Case (
not Mod ( $i ; 15 ); $substitute & "[" & $i & ";\"FizzBuzz\"];";
not Mod ( $i ; 5 ); $substitute & "[" & $i & ";\"Buzz\"];";
not Mod ( $i ; 3 ); $substitute & "[" & $i & ";\"Fizz\"];";
$substitute
)
ループを抜けた後で、 $substitute の末尾から ";" という文字列を除去したいので、以下の計算式で上書きします。
Left ( $substitute ; Length ( $substitute ) - 1 )
$result の計算式は以下のようになります。
Evaluate ( "Substitute(\"" & $result & "\";" & $substitute & ")" )
ただしこれだと、出力結果が以下のようになってしまいます。改行区切りのはずが、いつの間にやら半角スペース区切りに変換されてしまっているのです。
なので $result の計算式にもう少し手を加えて……
Substitute ( Evaluate ( "Substitute(\"" & $result & "\";" & $substitute & ")" ) ; " " ; ¶ )
よし、これで完璧だ!
……ん?
!!!
出力された各行についてはテキストとして取り扱われてしまうので、該当する数字が全て置換されてしまいます。
ざんねん!! わたしの ぼうけんは これで おわってしまった!!
パフォーマンス計測
最後に、パターンA、パターンB、爆死パターン、という3種類のパフォーマンスを計測してみます。
ルールは以下の通り。
- ループで 1,000 回実行させてみて、どれくらい時間がかかるか
パターンA
7秒くらい。
パターンB
16秒くらい。遅っ!
FileMaker で再帰ループしようとすると、スタック溜まってどんどん遅くなっていってしまうのですよね……あまりやらない方が良いです。
爆死パターン
10秒くらい。
いや出力結果は失敗してるんですけれど、再帰使うよりは高速で処理できそうですね。
結果まとめ
パターン | 処理速度 |
---|---|
A | 7秒 |
B | 16秒 |
爆死 | 10秒 |
以上のような感じになりました。
まったくもって、奇を衒わずに大人しくスクリプトループ使えって話ですね。
今回、冗談のようなアホのような事例で遊んでみましたが、実際に何か作る際の手法として何かの参考になればと思います。