フォルトンによる計算式の最適化

フォルトンに複雑な計算式をわかりやすくしてもらいましょう。
下記の問題について、とりあえず作った計算式の変数名を変更して、解説を行ってもらいました。
フォルトンは、変数名の変更とコメントを追加してくれました。

  • 問題のトピック
  • フォルトンの最適化

スレッドの内容

下記の2テーブルからテーブルデータを組み立てる式の変数名を6文字以内で妥当にものに。

LET(
  a,JSON_P(OTVAL(見積枚数)),
  b,JSON_P(OTVAL(使用材料)),
  tbl, ARRAY(),
  dmy, ARRAY_MAP(a,xx,
    ARRAY_MAP(b,yy,
      FSET(tbl, ARRAY(tbl,
        DIC(
          "枚数",DIC_ITEM(xx,"枚数"),
          "材料名",DIC_ITEM(yy,"材料名")
        ) 
      ))
    )
  ),
  tbl
)
rex0220 計算式プラグインの使い方 の発言:

思考時間: 19s

いいですね。6文字以内で“中身が分かる”名前に直した版です:backhand_index_pointing_down:

LET(
  maisu,  JSON_P(OTVAL(見積枚数)),   // 見積枚数テーブル → 配列
  zairyo, JSON_P(OTVAL(使用材料)),   // 使用材料テーブル → 配列
  rows,   ARRAY(),                   // 出力用の配列バッファ
  wk,     ARRAY_MAP(maisu,m,         // ダミー(副作用で rows を積む)
            ARRAY_MAP(zairyo,z,
              FSET(rows, ARRAY(rows,
                DIC(
                  "枚数T",   DIC_ITEM(m,"枚数"),
                  "材料名T", DIC_ITEM(z,"材料名")
                )
              ))
            )
          ),
  TABLE_DATA(rows)
)
  • maisu(5文字):見積“枚数”の配列
  • zairyo(6文字):使用“材料”の配列
  • rows(4文字):出力レコード配列
  • wk(2文字):ダミー評価用(結果は使わない)
  • ループ変数 m, z(各1文字)

いずれもフィールドコードと重複せず、6文字以内で役割が伝わる命名にしています。

あなた:

コメントは10文字以内で

rex0220 計算式プラグインの使い方 の発言:

了解です。コメントを10文字以内にした版はこちらです:backhand_index_pointing_down:

LET(
  maisu,  JSON_P(OTVAL(見積枚数)),   // 枚数取込
  zairyo, JSON_P(OTVAL(使用材料)),   // 材料取込
  rows,   ARRAY(),                   // 出力用
  wk,     ARRAY_MAP(maisu,m,         // 外側回
            ARRAY_MAP(zairyo,z,      // 内側回
              FSET(rows, ARRAY(rows,
                DIC(
                  "枚数T",   DIC_ITEM(m,"枚数"),
                  "材料名T", DIC_ITEM(z,"材料名")
                )
              ))
            )
          ),
  TABLE_DATA(rows)
)
あなた:

計算式の解説を

rex0220 計算式プラグインの使い方 の発言:

もちろんです。
上記の計算式の目的は、2つのテーブル(「見積枚数」と「使用材料」)から、組み合わせテーブルを作ることです。
つまり、「見積枚数 × 使用材料」の全ての組み合わせを新しいテーブルとして生成しています。


:magnifying_glass_tilted_left: 式の全体構造

LET(
  maisu,  JSON_P(OTVAL(見積枚数)),   // 枚数テーブル配列
  zairyo, JSON_P(OTVAL(使用材料)),   // 材料テーブル配列
  rows,   ARRAY(),                   // 出力用配列
  wk,     ARRAY_MAP(maisu,m,         // 外側ループ
            ARRAY_MAP(zairyo,z,      // 内側ループ
              FSET(rows, ARRAY(rows,
                DIC(
                  "枚数T",   DIC_ITEM(m,"枚数"),
                  "材料名T", DIC_ITEM(z,"材料名")
                )
              ))
            )
          ),
  TABLE_DATA(rows)                   // テーブルデータ返却
)

:puzzle_piece: 各部分の解説

変数 内容 説明
maisu JSON_P(OTVAL(見積枚数)) 「見積枚数」テーブルを配列として取得
zairyo JSON_P(OTVAL(使用材料)) 「使用材料」テーブルを配列として取得
rows ARRAY() 出力結果を格納するための空配列
wk ARRAY_MAP(...) 2重ループ処理(結果は使わない)
TABLE_DATA(rows) 最終的に作った配列をテーブル形式に変換

:repeat_button: 2重ループの動き

ARRAY_MAP(maisu,m,
  ARRAY_MAP(zairyo,z,
    FSET(rows, ARRAY(rows,
      DIC("枚数T",DIC_ITEM(m,"枚数"),"材料名T",DIC_ITEM(z,"材料名"))
    ))
  )
)
  1. 外側 (maisu):見積枚数テーブルの各行を m として処理
  2. 内側 (zairyo):使用材料テーブルの各行を z として処理
  3. 組み合わせ生成
    m(枚数)と z(材料)を組み合わせて、1行の辞書(DIC)を作成

結果はこのような形になります:

枚数T 材料名T
枚数1 材料A
枚数1 材料B
枚数2 材料A
枚数2 材料B

:brain: 関数のポイント

関数 説明
OTVAL() 他テーブルの値を取得
JSON_P() JSON文字列を配列化
ARRAY() 空配列を生成
ARRAY_MAP() 各要素に対して繰り返し処理
FSET() 配列に要素を追加
DIC() 辞書(オブジェクト)を作成
DIC_ITEM() 辞書内の指定項目の値を取得
TABLE_DATA() 配列をテーブル形式データに変換

:white_check_mark: この式の最終結果

  • 出力テーブルには、見積枚数テーブルと使用材料テーブルの全組み合わせ行が生成されます。
  • 組み立てたデータは「TABLE_DATA」で出力し、
    他のフィールドに「テーブル型」で設定すれば、そのまま一覧表示できます。

希望があれば、この式を
:blue_book:「各材料ごとの合計枚数を集計する式」や
:blue_book:「条件付きで組み合わせを絞る式」
に発展させることもできます。
やってみますか?

あなた:

この計算式で改善できる点は?

rex0220 計算式プラグインの使い方 の発言:

思考時間: 40s

いい質問です。主に「無駄なループ更新を減らす」「空配列時のガード」「フィールドコードの明確化」で改善できます。

改善ポイント

  1. FSET呼び出し回数を削減
    今は 枚数×材料 の全組合せぶん FSET しています。
    外側ループ1回につきまとめて rowsへ結合すると速く・短くなります(ARRAY は配列同士をマージできます)。
  2. 空配列ガード
    どちらかのテーブルが空なら即 TABLE_DATA([]) を返せば無駄計算を回避できます(ARRAY_MAP は要素ごとに処理するため)。
  3. フィールドコードの厳密化
    DIC("枚数T",..., "材料名T",...) のキーはテーブル内項目のフィールドコードに合わせると安全です(ラベルではなくコードを使うのが原則)。
  4. コメントは10文字以内
    ご要望どおり短く(既に対応済み)。

最適化後の式(FSET回数削減+空配列ガード)

LET(
  maisu,  JSON_P(OTVAL(見積枚数)),   // 枚数取込
  zairyo, JSON_P(OTVAL(使用材料)),   // 材料取込
  rows,   ARRAY(),                   // 出力用
  e,      IF(ARRAY_COUNT(maisu)=0 || ARRAY_COUNT(zairyo)=0,
              1,                     // 空ならスキップ
              ARRAY_MAP(maisu,m,     // 外側回
                FSET(rows, ARRAY(rows,
                  ARRAY_MAP(zairyo,z, // 内側回
                    DIC(
                      "枚数T",   DIC_ITEM(m,"枚数"),
                      "材料名T", DIC_ITEM(z,"材料名")
                    )
                  )
                ))
              )
            ),
  TABLE_DATA(rows)                   // 返却
)
  • 外側1回につき FSET も1回に圧縮(計 #枚数 回)。
    ARRAY(rows, ARRAY_MAP(...))配列をマージしているため、ネストせず平らに積めます。
  • ARRAY_MAP を使った配列処理の基本はこの流れ(配列→要素ごと関数適用)。

必要なら、出力キー("枚数T", "材料名T")を実際のフィールドコードに置き換えてご利用ください。

あなた:

テーブルには1行以上存在するので、下記の判定は不要 IF(ARRAY_COUNT(maisu)=0 || ARRAY_COUNT(zairyo)=0,

rex0220 計算式プラグインの使い方 の発言:

はい、その通りです。
「見積枚数」「使用材料」テーブルの両方に必ず1行以上ある前提であれば、
空配列チェック(IF(ARRAY_COUNT(...)=0 ...))は不要です。

したがって、よりシンプルで効率的な最適版は以下になります:backhand_index_pointing_down:


:white_check_mark: 改善後の計算式(空チェック削除・FSET回数削減)

LET(
  maisu,  JSON_P(OTVAL(見積枚数)),   // 枚数取込
  zairyo, JSON_P(OTVAL(使用材料)),   // 材料取込
  rows,   ARRAY(),                   // 出力用
  wk,     ARRAY_MAP(maisu,m,         // 外側回
            FSET(rows, ARRAY(rows,
              ARRAY_MAP(zairyo,z,    // 内側回
                DIC(
                  "枚数T",   DIC_ITEM(m,"枚数"),
                  "材料名T", DIC_ITEM(z,"材料名")
                )
              )
            ))
          ),
  TABLE_DATA(rows)                   // 出力
)

:light_bulb:この改善のポイント

改善項目 内容 効果
FSET回数削減 外側ループ1回ごとにまとめて追加 実行速度向上(内部ループ回数分のFSETを削減)
不要なIF削除 常に1行以上ある前提なので判定不要 式が短く・読みやすくなる
短い変数名維持 maisu, zairyo, rows, wk 可読性と制約(6文字以内)の両立

:brain: 結果の動き(簡易イメージ)

  • 「見積枚数」テーブルに3行
  • 「使用材料」テーブルに2行
    → 結果:3×2=6行の組み合わせが出力されます。
枚数T 材料名T
枚数1 材料A
枚数1 材料B
枚数2 材料A
枚数2 材料B
枚数3 材料A
枚数3 材料B

この形が、現時点で最短・最速・可読性のバランスが取れた式です。