FileMaker 18の新機能: SetRecursionとWhile

FileMaker 18で導入されたWhile関数によって、従来はカスタム関数として書かなくてはいけなかった式を、通常の計算式として扱うことができるようになりました。今回は、FileMakerHacksのKevin Frankさんによる記事を紹介します。


FileMaker Hacks logo

New in FM 18: SetRecursion and While

(元記事はこちら)

Kevin Frank
2019/5/22

注: 新しいWhile関数の登場によって、古くからあるCustomListカスタム関数(これについては数年前に詳しい記事を書きました)との比較にどうしても興味がいきます。簡単なテストでは、特定の状況下ではCustomListの方が高速であり、他の状況下ではWhileの方が高速であることが示されました。私の意見では、どちらも開発者のツールキットに入れておくべきです。

今回は、FileMaker 18で導入された2つの新しい関数、 SetRecursionWhileを見ていきます。興味のある方はデモファイルで実際に試してみてください。

maclaurin-series.png

デモファイル: While-Sandbox.zipMaclaurin-Series.zip

 

SetRecursion ( 式 ; 最大繰り返し )

背景: 2004年にFileMaker 7がリリースされて以来、再帰には2つの制限がありました。標準的な再帰の繰り返しが10,000回、末尾再帰の繰り返しが50,000回です。FM 18のリリースにより、これは2つの点で変わりました。

1. 10,000回の制限がなくなりました。デフォルトの繰り返しの制限は、再帰型かどうかに関わりなく50,000回になりました。

2. この制限は、SetRecursionを使用して増減できるようになりました。引数「最大繰り返し」のドキュメント化された上限は確認されていませんが、私自身の実験では最大で5億回(はい、5億回です)の反復が成功しています。

まず「最大繰り返し」について触れました…では「式」とは何でしょうか? 式には、任意の有効な計算式を設定することができますが、実際には式が再帰を使用しない限り、つまり1つ以上の再帰的カスタム関数やWhile構文を含まない限り、SetRecursionを使用する意味はありません。

例を示します。対象レコード全体からJSON配列を生成する末尾再帰的なカスタム関数があるとします。それを“FoundSetToArray”としましょう。そして話を単純にするために、このカスタム関数は引数を持たないと仮定します。カスタム関数がレコードごとに1回繰り返されるとすると、対象レコード数が50,000件以上の場合、FM 17以前では「?」が返されました。

FM 18以降では、対象レコードの件数に関係なくそのタスクを実行できます。

SetRecursion ( FoundSetToArray ; Get ( 対象レコード数 ) )

注: この機能により、FileMaker社は我々開発者を吊るすための事実上無限の長さのロープを与えました。気をつけなくてはいけないのは、実行時間または使用可能なメモリを超える可能性の観点から、ここでノーリスクというものは存在しません 。合理的な期待値、多少の注意、および適量の常識を持って作業を進めてください。

While ( [ 初期変数 ] ; 条件 ; [ ロジック ] ; 結果 )

While関数は、指定された条件が満たされている限り、タスク(「ロジック」)を繰り返し実行します。条件が満たされなくなると、結果が表示されます。

FileMaker 18より前のバージョンでは、計算エンジンで再帰の力を利用したい場合は、カスタム関数を定義する必要がありました。While関数が登場した今、再帰は計算エンジン内で直接使用でき、明快で使いやすい実装になりました。

最初は関数の構造が少し混乱しやすいと感じるかもしれませんが、時間をかけていくつかの例を検討し、データビューアでいくつかの式を作成すると、すぐに理解できるようになります。While関数は論理的かつ非常に強力で、カスタム関数を書いてデバッグするのに比べると非常に時間を節約できます。

基本的な例

2つのほぼ同じ内容の例があります。式の目的は、変数を1から10に増やしてから、その変数の内容を表示することです。

side-by-side.png

一つの式(Expression)の1番目と3番目の引数に[ ]がついているのはなぜでしょうか? これは、これらの引数にオプションで複数のエントリをセミコロンで区切って含めることができることを示しています。単一のエントリしか含まれていない場合、多くの開発者は開発時の指針として[ ]をそのままにしていますが、厳密には必要ありません。

注釈付きの例: コイン投げ

この例では、引数1と引数3に必要な[ ]を使って(複数のエントリが含まれているため)、100回のコイン投げをシミュレートしています。

annotated-example-400

H = 表(heads)、T = 裏(tails)

説明:

  1. 乱数を生成する(例えば、.95258565133063877184)
  2. Middle関数を使用して、小数点の右側の最初の数字を取得する
  3. Mod関数を使用して、上の値を2で除算する
  4. 結果(つまり、余り)は0か1になる
  5. Choose関数で、0の場合はH、1の場合はTを割り当てる
  6. この値を、xに格納されている累積された文字列の末尾に追加する
  7. iが100を超えるまでステップ1から6を繰り返し、超えたら抜け出してxを表示する

注1: 最初の変数の宣言で開始値を代入しますが、[ロジック]セクションでこれらの変数を、変換されたバージョン(つまりx = x &…)に設定して反復的に更新します。Let関数を使って変数を定義するのに慣れているのであれば、2つの[ ]で囲まれたセクション(上図の黄色で強調表示された部分)は見た覚えがあるでしょう。

注2: [ロジック]セクションの最後のエントリは、多くの場合 “i = i + 1″、またはカウンタを増やすための何らかの類似した構造体(別名イテレータ)で、記述された終了条件に1ステップずつ近づいていきます。

Whileに慣れようとしている間に私が頻繁に犯した間違いは、イテレータを増やすのを忘れることでした。それについてはデフォルトの50,000回の繰り返しの上限に感謝しました(これによってFileMakerが無限にロックされるのを防いでくれるからです…もちろん、SetRecursionを使ってデフォルトの制限を上書きしていない場合ですが)。

2のべき乗

もう少し例を見てみましょう。以下の式(Expression)は、2の最初の10回のべき乗を、スペース区切りのリストとして表示します。

2019-03-20_211320

FizzBuzz

次はJohn WeinshelによるFizzBuzzの練習問題を実装したものです。数字を順番に数えていき、

  • 数字が3の倍数の場合は、Fizzと言う
  • 数字が5の倍数の場合は、Buzzと言う
  • 数字が3と5の両方の倍数である場合は、FizzBuzzと言う
  • いずれでもなければ、単に数字を言う

2019-03-20_170033-fizzbuzz.png

Whileの中のWhile

コイン投げの例に戻ると…100個のコインを100回投げたいとしたらどうでしょうか。一方のWhile文をもう一方の中に埋め込むことはできるでしょうか? もちろん可能です。内側のwhile(赤で囲った部分)は、前の例から変更なしに再利用できます。

2019-03-10_whilewhile.png

単語インデックスの生成

次に、ジェーン・オースチンの「高慢と偏見」の最初の章を利用します。

オースティン-1

ここで、単語の長さの昇順にソートされた単語のインデックスを作成します。

オーステン-2

説明:

  1. ユニークな(小文字の)単語リストを生成する
  2. 各単語の前に、単語の長さに等しい数のbyte order mark (BOM)を付ける
  3. locale引数にUnicode_Rawを使用して、SortValuesでリストをソートする
  4. BOMを数字と置き換える
  5. RayValologonが作成したカスタム関数Trim4を使って、SortValuesとUniqueValuesによって追加された邪魔な末尾のリターンを取り除く

注意: FileMaker Hacksには、今までにもbyte order markがここ(It’s Sorta A Value List Thing)やここ(Custom Field-Based Value Lists)で登場しています。

2019/6/2更新: 振り返ってみると、上の例でLetを使う必要はありませんでした。すべての変数はWhileで宣言することができます。 PNG, .

素数ジェネレータ

以下はもう一つ、Whileの中にWhileを入れる例です。今回は、2から指定された制限までのすべての素数を生成します。これは必要最小限の概念実証であり(つまりほとんど最適化されていない)、以前のCustomListの記事で使用されているものを別のアプローチで行っています。

素数生成-v3.png

説明:

  1. 外側のWhileは、3から指定された上限までの奇数の候補リストを生成する
  2. 内側のWhileは、各候補をその平方根以下のすべての奇数で除算することによってテストする
  3. 2は特別な場合(唯一の偶数の素数)であるため、結果が表示されるときに先頭に追加される

さらなる最適化についての考察

  • 外側のWhileで奇数の候補をすべて生成するのではなく、常識を適用して3, 5, 7, 11の倍数を除外することもできますが、どこで線を引くか迷うところです。
  • 外側のWhileロジックをさらに改良して、式がMod ( 候補 ; 6 ) = 1またはMod ( 候補 ; 6 ) = 5となる候補だけを生成する方法もあります。これは3より大きいすべての素数は6で除算するとあまりが1または5になるためです。[残念ながら、多くの合成数(composite number)でもこの式が成り立つので、これは素数を生成するための魔法のレシピではなく、あくまでも候補を生成するためだけにしか使えません。]
  • 上記のどちらかを実装すると、内側のWhileロジックを高速化できます。

マクローリン級数(Maclaurin Series)のグラフ化

maclaurin-series.png

このデモはOliver Reidの好意によるもので、以下の注釈をつけてくれました。

  1. yの値の計算では、 入れ子のWhile関数を使用する。1つはx値ごとにy値を計算し、もう1つはxの各値の級数の合計を計算します。
  2. マクローリン級数は実際のアプリケーションで実際に値を計算するには非効率的な方法であることが多く、時系列はゆっくり(これはxが0から離れているほど顕著)収束する可能性があり、8πに達するのに50周期よりもずっと多くの回数が必要です。
  3. https://ja.wikipedia.org/wiki/テイラー展開

WhileでSetRecursionを使う

最初にカスタム関数でSetRecursionを使用する例を示しましたが、これはWhile関数と同じように機能し、次の形式を取ります。

SetRecursion ( While ( ... ) ; 最大繰り返し )

ちなみに、以下が私の古いWindows 7のラップトップで51分以上かかって5億回の繰り返しを行った式です。

[余談: 100万(million)と10億(billion)の違いを理解する方法…100万秒はおよそ11日半、10億秒はおよそ32年です]

最後に

While関数に私がつけたニックネームは「一般人向けの再帰」です。この機能を利用して、開発者コミュニティから非常に独創的なソリューションが生まれてくると私は予測しています。おめでとう、FileMaker社。大変よくできました。

Leave a Reply