LabVIEWのMap (マップ) とは?

私はファイナルファンタジーX (FF10) が大好きです。やりこみプレイをするぐらい好きで、例えば、スフィア盤縛りでモンスター訓練場のオリジナルのモンスターたちと戦ったり、装備なし(正確には装備アビリティなし)のリュック単騎ですべてを超えし者を倒したりしました。私の中では装備なし+単騎ですべてを超えし者を倒せるのは大召喚士ユウナ様(+召喚獣)とリュックだけと結論付けています。

FF10でリュックを活躍させるには調合のレシピを知る必要がありますが、パターンが多すぎてとても覚えられません。アルティマニアをもとにExcelでシートを作って検索できるようにしたものです。

FF10調合の表の例

このExcelシートのスクリーンショットを見ると、パターンが多すぎて調べるのが大変というのが分かると思います。そこで、プログラム的に検索できるようにしたことがあります。検索用のプログラムはデスクトップアプリができるものなら好きなプログラミング言語で作ればいいと思いますが、LabVIEWではどうやって作るのがいいのでしょうか?従来(具体的には2018年以前)なら上の表を2次元配列として持たせると思いますが、今はMapというデータ型を使うとより効率的に検索できます。そこで、今回はLabVIEWのMapについて紹介します。

Mapとは?

LabVIEWにおけるMapとは、次のようなデータ型です。

  • キー+値のペアの集合である
  • キーはユニークである
  • 各キーは対応する値を持つ

Pythonなどでいうdictionary型(辞書型)に相当するデータ型です。あらかじめMapにキーと値を登録しておき、必要になったときにキーを指定することで値を取得できるというわけです。キーはユニークで重複を認めないので、同じキーに値を書き込む場合は既存キーの更新をすることになります。また、Mapに登録した順番には意味はなく、検索しやすいようにキー+値が登録されます。キーについて昇順に並べているような感じです。

Mapの利点

例えば、こんなブロックダイアグラムを見たことがある人もいるかもしれません。

Module 1のエラーを登録、更新する処理
上2つがMapを使わない方法。下がMapを使った方法。

上のブロックダイアグラムには3種類の処理がありますが、どれもModule 1のエラーを更新するものです。正確には、初めてModule 1のエラーを書き込む場合は登録し、登録済みなら値を上書きするというものです。上2つはMap実装以前の方法で、Map実装以前にタグやキーなどと値を対応させて持たせるには、キー用の配列と値配列を別に持たせるか、キーと値をクラスタにした配列を持たせるぐらいしか方法がありませんでした。Current Value Table (CVT、現在値テーブル)と呼ばれる現在値を複数モジュールで共有するためのライブラリでも、従来方法によってタグ(あるいはキー)と値を対応させてます。これが、Mapの場合はInsert Into Mapだけで実装できます。これはMapにキーがあれば値を更新し、なければMapに追加するというものです。キーの重複について気にする必要がなくなります。

検索の場合は次のようになります。

キーに対応する値の検索

従来法の場合は、キーに対して1次元配列検索やForループで1つずつ探索 (線形探索) する必要がありましたが、MapではLook In Mapでキーを手がかりに探してもらうだけです。Mapではキーに対して検索しやすいようにデータを保持しています。二分木探索のようなことができ、効率的に検索できます。…と言いきっていいのかな?実際はどうやって探索してるんでしょうね?

Mapを使ってみる

定数および関数

実際にブロックダイアグラムにMapを置いてみます。

マップの例。上が関数パレットから置いたもの。
下はそこからキーに列挙体、値にエラークラスタをドラッグ&ドロップしたもの。

関数パレットにあるMap定数をブロックダイアグラムに置くと、キーが文字列、値が数値のMapが配置されます。このMap定数のキーおよび値に実際に使用したいデータ型の定数をドラッグ&ドロップすることで、キーと値の種類を変更することができます。上の図の下では列挙体をキーに、エラークラスタを値にしています。キーと値にするデータ型は何でもいいらしいです。配線色は、値に設定した型で決まります。

Mapには次の関数があります。主なものは以下の通りです。

  • Build Map:キーと値を入力してMapを作成します。ただ、規模の大きいシステムでこれを使うことは個人的にはおすすめしないです。
  • Insert Into Map:マップにキーと値を登録します。キーがマップに登録済みの場合は、既存キーの値を更新します。
  • Remove From Map:マップからキーと値を削除します。キーがマップになくてもエラーにはならず、key not found?出力がTrueになるだけです。
  • Look In Map:マップからキーに対応する値を取得します。キーがマップにない場合は、key not found?出力がTrueになり、入力で設定したデフォルト値が出力されます。
Mapの関数
Look In Mapのヘルプ

…Map関数でキーが存在するかどうかの出力が”key not found?”なのは仕様です。この仕様により、私はこの出力がTrueのときに取得ができたのか分からなくなることがあります。Trueならマップに存在しないです。ブールの出力をnotにするのは分かりにくいですね。

Forループ

Mapに登録されているキー+値を全て取得するには、マップをForループに入力します。

MapをForループに入れる。

MapをForループに入れる際にIndexingを有効にすることで、Map内のキー+値を取得できます。名前でバンドル解除したときの名前については、Map作成時にドラッグ&ドロップでいれたキーおよび値のラベル名になります。MapをMapとしてForループ内で使う(検索処理を繰り返し行うなどをする)場合はIndexingを無効にします。また、Forループ内でMapを更新していく場合はシフトレジスタを使用します。このあたりは従来の1次元配列と同じです。

In-Placeストラクチャ

MapではIn-Placeストラクチャで値を更新することもできます。

In-Placeストラクチャ

In-Placeストラクチャでキー登録済みの値の一部だけを変えたり、現在の値を基準に+1などをしたりすることができます。出力側の入力端子に”action”というものがあります。actionには次のものがあります。

  • Modify Entry only if found:キーが登録済みのときだけ処理をします。未登録の場合は何もしません。キーの追加をしないということです。未配線の場合はこの処理になります。
  • Add or Modify Entry:キーがあればそのキーの値をIn-Place処理し、なければデフォルト値をもとにIn-Place処理をしてMapに追加します。
  • Delete Entry:入力キーと値をMapから削除します。

In-PlaceストラクチャでMapを更新したと思ったらMapに追加されていなかった…ということが起こるとしたら、actionを確認すると有効かもしれません。

Mapを使った例

ATMのサンプル問題 (CLD)

CLDの問題にATMのシミュレーションを行う問題があります。このサンプル問題では、口座情報をテキストファイルから読み取ります。

ATMサンプル問題のテキストファイル

このファイルを読み取って口座情報を照合したり更新したりするわけですが、Mapを使うとうまく取り扱うことができます。

ATMファイル読み取り+Map登録

ATMの口座情報をMapで取り扱うことで、IDと口座情報の紐づけ、IDをキーに残高の照会や更新処理をうまく行えます。CLDサンプル問題の公式回答ではMap実装以前に作られたものの可能性があるので、そこをMapに置き換えると、ブロックダイアグラムがよりシンプルになります。

複数のキーを持たせる

ここまでの紹介で、Mapにはキーが1つしか持てないと読み取れると思います。これはその通りですが、裏技的?にキーを増やすことができます。それは…

配列やクラスタをキーにする

です。キーも任意の型が使えるので、1データに複数の要素がある型をキーにすれば、実質的に複数のキーを使用できます。冒頭のFF10の調合はアイテム2つを使うので検索するにはキーが2つ必要ですが、2つのアイテムを1つのクラスタにまとめれば、Mapで検索できるようになります。

実質複数キー使ったMap

さすがに調合全パターンをマップに登録するのは手間がかかりすぎるので、3種類だけにしました。上記の場合、Item1に勝負師の魂、Item2にポーションというパターンを登録していませんが、Look In Mapの直前でkeyのItem1を値の小さいほう、Item2を値の大きいほうに入れ替えることで、無駄な登録をしなくて済みます。とはいってもkeyのItemの入れ替え処理が発生するので、メモリを優先するか、実行速度を優先するかでどうするかが変わると思います。まあ、この程度だったらどっちでもいいと思いますが。

クラスタや配列で検索する場合は、完全一致するものしか取得できません。本来は1つだけのキーに無理やり複数要素があるものを使っていますからね。部分一致は想定していないと考えられます。部分一致のリストを取得する場合はおとなしくForループで全部取り出して配列処理でやるしかないですね。

Mapを使うときの注意点

規模の大きいプロジェクトではタイプ定義にする

この記事内では、Build Mapを使わずに空のMap定数にInsert Into Mapでキー+値を追加する形でMapを作成していますが、これには訳があります。それは、

Mapはタイプ定義にしたほうがいい

からです。Build Mapではタイプ定義のMapを作れないため、Build Mapを使わずにMapを作っていたわけです。なぜ、Mapをタイプ定義にしたほうがいいかというと、キーや値の型を変えたときにほかのサブVIに影響することがあるためです。タイプ定義にしていないと、サブVIで使っているMapをすべて手作業で変更する必要がありますが、タイプ定義にしていればMap部分に関してはタイプ定義を変えるだけですべてのMapに変更が適用されるためです。これはクラスタをタイプ定義にしたほうがいいのと同じです。

ATMの口座情報の場合、例えば、ID(キー)を文字列から数値に変更しようとした場合、Mapをタイプ定義していないと、上の2つのサブVIのマップのキーを変更する必要があります。これはサブVIが増えるほど変更の手間が大きくなります。また、この手の作業は手作業になりがちなので、変更の抜け漏れが発生するリスクがあります。タイプ定義にしていれば、Mapに関してはタイプ定義を変更するだけで対応できます。IDの入力は壊れますが、これは壊れた部分だけ対応すればいいので、抜け漏れのリスクは減らせます。なので、タイプ定義にすることをおすすめします。…まあ、タイプ定義のファイル数が多くなりますが。

ブロックダイアグラム上のMap定数を編集できない

生成するMapが固定であっても、キー+値をForループで入れていました。Mapが固定なら最初からその定数を作ればいいのですが、なぜそうしなかったのかというと、理由があります。それは、

フロントパネル/ブロックダイアグラムでMap制御器/定数を手作業で編集できない

からです(LabVIEW 2026 Q3時点)。キーや値のデータ型はドラッグ&ドロップで変えられますが、中身の値は変えられません。なので、空のMapからプログラム的に作るしかないかというと、空ではないMap定数を作る方法があるにはあります。次の手順で作れます。

  1. まずは、空のMapからプログラム的にMap表示器を作る。
  2. できたMap表示器を定数に置換する
空でないMapを作ってみた

こうすることで、空ではないMapができます。修正はできないので、定数を変える場合は再びプログラム的に作ります。正直なところかなり手間で、コンパイラがいつも同じ結果になる処理を定数化してくれることを信じたほうがいいかもしれません。それでもMap定数をフロントパネルやブロックダイアグラム上で編集したい場合は、

Edit A Set Value.llb / Edit A Map Value.llb – NI Community

このプラグインが使えるかもしれません。

まとめ

ここでは、LabVIEWのMapについて紹介しました。個人的にはかなり便利な機能だと思っています。これまでキーと値を対応させて登録するような処理を配列で実装したこともありますし、そうしているブロックダイアグラムも見たことがあります。登録済みかを確認してそれによって条件分岐するのはわずらわしいとずっと思っていたので、Mapによってそれが解消されるのはありがたいと感じています。

ただ…key not found?の出力にはずっと頭を悩ませることになりそうです。

タイトルとURLをコピーしました