この記事で扱っていること
- LabVIEWでスタックを扱う方法
を紹介しています。
注意:すべてのエラーを確認しているわけではないので、記事の内容を実装する際には自己責任でお願いします。また、エラー配線は適当な部分があるので適宜修正してください。
プログラミング言語で複数のデータを扱う方法として、「先入れ先出し」のメモリ領域を用意しておくキューの仕組みはLabVIEWで標準的な関数として実装されています。
一方で、「後入れ先出し」の仕組みはよくスタックと呼ばれたりしますが、このスタックはLabVIEWでは標準関数として実装がありません。
無ければないで、こういった仕組みを作ってみようということで作ってみました。
どんな結果になるか
いくつかのデータタイプでスタックの仕組みを使ってデータを扱った際の結果を示します。なお、使用している関数には、FirstInLastOutの略でFILOという名前を付けています。
まずは簡単に浮動小数点で試した結果です。
「要素を入れる」とすることで値を入れられますが、取り出すときには入れられた順が早いデータほど後から取り出されることになります。
また、「要素を後方に入れる」とすることで、強制的に一番最後に取り出すようにすることもできます。
取り出す際には、入れたデータのデータタイプを指定する必要があります。
配列データを扱うこともできます。
また、複数のデータタイプを含んだクラスタでも機能します。
「要素をまとめて取り出す」を行うことでそれまでに入れたデータを一度に取り出し、それから再びデータ要素を入れていくことができます。
プログラムの構造
LabVIEWの標準関数であるキューは、キュー取得、エンキュー、デキューといった操作がそれぞれ関数として存在していますが、スタックについては機能的グローバル変数の形で実装しました。
データを保持しておくのは配列としています。配列操作の関数を駆使してスタックを実現する、というわけですね。
機能的グローバル変数の中身のステートをそれぞれ紹介します。
まずは生成ステートです。キューでいうところのキュー取得の機能にあたります。
どんなデータタイプでもバリアントで受け取りこれを配列とします。が、このケースで受け取ったデータは実際には使用しません。キュー取得で、「要素データタイプ」に配線したデータを使用しないのと同じです。
また、後で使用する各種フラグを初期化します。
次に要素を入れるケースです。キューでいうエンキューの操作を行います。
現在の要素数が0かそれ以外かで処理を変えます。
次に要素を後方に入れるケースです。キューでいう、先頭に要素をエンキュー、のスタックバージョンです。
要素を入れるケースと配列連結の上下を逆にしただけですね。
次に要素を取り出すケースです。デキューにあたります。
要素数0フラグのブール値によって処理内容を変えています。もし要素数0フラグがFalseの場合には、配列に有効なデータが1つ以上含まれているので、それを取り出し、「新規要素?」をTrueとするようにしています。
この機能的グローバル変数の作りだと、エンキューされるかタイムアウトしないと実行されないデキューの関数と違って、要素数が0でも実行することはできてしまいます。その時にこの要素を取り出すステートで出力される「取り出した要素」がちゃんと意味のある値であるかどうかを判断するために「新規要素?」のブール値を設けています。
次は要素をまとめて取り出すケースです。キュー排出にあたります。
要素サイズを取得ケースです。キューステータス取得にあたります。配列のサイズを表示するだけですね。
最後に終了ケースです。キュー解放にあたります。
エラーは、エラーリングを使用して特別なエラーメッセージを作っています。エラーリングについてよくわからないよ、という方は以下の記事を参考にしてみて下さい。
なんでわざわざエラーを出させるようにしたのか?それはこの後紹介するループ間でデータを渡すような使い方をする場合にエラーが出るということを利用してループを止めるためです。
ループ間でデータを渡す場合
キューはループ間でデータを共有する際に便利な機能です。
同じようなノリでスタックもループ間でデータを受け渡しできるのですが、上で紹介した作りの場合、消費者ループにあたるほうのループ速度をタイミング関数で制限する必要がある、という注意点があります。
ループ間でのデータの受け渡しの様子を見るために、以下のようなプログラムを書いてみます。
生産者ループにあたるほうを停止ボタンで止めた後、終了ケースが実行されるため、これにより消費者ループにあたるほうではエラーが発生しループを止めています。
チャート表示することで、配列の形式で一度に複数のデータをスタックに入れることができます。このとき、消費者ループにタイミングの関数(上の図でいう、200を入力した次のミリ秒倍数まで待機の関数)がない場合には、スタックの動作をしません。
読み取りがひっきりなしに動いているので入れた値をすぐに読み取ってしまうから、ということになるかと思います。
そのため、このようなループ間での受け渡しを行う場合には、消費者ループが値を早く読み出しすぎないように「次のミリ秒倍数まで待機」などを使用してある程度読み出しの頻度を落としてやることで、期待する結果が得られます。
どの程度ループの速度を落としたらいいかは、タイミングの関数への入力値をいくつか変えて試してみてください。
本記事では、LabVIEWにおいてスタックの仕組みを実装する例を紹介しました。
機能的グローバル変数の仕組み上、非再入実行のviとなるため、要素を入れるのと要素を出すのと全く同時に操作することはできませんが、参考になればうれしいです。
ここまで読んでいただきありがとうございました。
コメント