本シリーズでは、このブログで紹介してきた「まずこれ」のシリーズを凝縮して、LabVIEWによるプログラミングはこんな風にできるんだ、ということを比較的すぐに体験できるよう、内容を絞って紹介しています。
また、紹介する画面はLabVIEW Community Editionという、非商用の目的であれば無料で使用できるエディションを使っています。なので、LabVIEWに興味を持ったらCommunity Editionを実際にダウンロード、インストールして触りながらプログラムを作り、LabVIEWの楽しさに触れてもらえればと思います。
第三回の今回から、いよいよプログラムらしいプログラムを書くための基本的な要素を紹介していきます。
手始めに今回は、「処理を繰り返す」方法を紹介していきます。が、その前段階として、複数の値のまとまりを扱うための配列というデータタイプについて紹介します。
なお、前の記事はこちらです。
複数のデータをひと固まりに扱うためのデータタイプ:配列
プログラムでは、何か特定の処理を繰り返すということが頻繁に起こります。
しかし、プログラムはいつかは終わるわけで、繰り返しも永遠に行われるということはありません。
処理を繰り返すという仕組みは記事後半でより詳しく見ていくとして、何らかの基準で有限回の繰り返しが終わった後、問題になるのはその繰り返しによって得られたデータの集まりの扱い方です。
つまり、「繰り返すことによって結果がたくさん得られる」わけですが、これらをどう適切に扱うかについてまずは注目してみます。
例えば「足し算を二回繰り返したら、一回目の結果と二回目の結果、合計2個の結果が得られる」ということになりますし、「足し算を百回繰り返したら、合計100個の結果が得られる」ということになります。
このとき、例えば数値を表示するのはNumeric Indicator、数値表示器だったわけですが、じゃあ足し算を100回繰り返したときにそれら一つ一つの結果を表示するには、数値表示器を100個用意する必要がありそうですね。
であればと、フロントパネルにせっせと数値表示器を100個置いて・・・
いや、これは全く現実的ではないということに気づくと思います。もちろん、頑張ればフロントパネル上に100個の数値表示器を置くことはできますが、それだけで一苦労ですし、例えば「29回目の結果はどれだ」ということを調べたい場合に、とても扱いにくい状態となってしまいます。
「そんなピンポイントで何回目の結果を見たいなんてことあるの?」と思うかもしれませんが、実際問題結構あります。それ以外にも例えば「10回目から20回目の結果を表示してほしい」とか「偶数回目の結果は2倍にする」みたいな処理を行うことがあるかもしれません。
要は、複数のデータの集まりに対して特定の操作をすることでその一部のデータに処理を加える、という操作が必要な場合があります。
下の図は、データの数を数えやすいように上の行に番号を、下の行に何かしらのデータ(ある計算)を表示した表でデータのまとまりを表しています。
そんなとき、個々の値をいちいち数値表示器に表しておくのはあまりに不便です。
そんなときに役立つのが、これから紹介する配列というデータタイプです。
この配列には、「要素」と「指標番号」という二つのキーワードがあります。
配列は、イメージとしては「番号が付いた箱がたくさんある」状態です。それぞれの箱に、値を入れていきます。足し算100回の結果を配列にする場合、「1回目の足し算の結果」を箱にいれ、「2回目の足し算の結果」をまた別の箱に入れ、という作業を100個の箱に対してするイメージです。
まさにすぐ上の図で紹介したものと全く同じような状態です。
この時の、各箱に入った値を「要素」と呼んで、「何回目の足し算の結果」の「何回」にあたる数字を「指標番号」と呼びます。
ただし、通常箱の数を数える際には1個目の箱、2個目の箱、…などと数えていくのに対して、指標番号は0から数えるので0個目の箱、1個目の箱、などと数えていきます(そのため、通常の数え方と1だけ数字がずれます)。
実際にLabVIEW上でどのように配列を用意してどのように扱うかを紹介します。
新規のプログラム(vi)を開いて、フロントパネルを開きます。右クリックして出てくるのは制御器パレットでした。この制御器パレット上でData Containersを選択してその中にあるArrayを選択、フロントパネル上の好きな位置に置きます。
Arrayは英語で「配列」のことです。
そうしたら今度は、配列として扱いたいデータタイプを制御器パレットから探します。
例えば数値の配列を使いたかったら、数値の制御器であるNumeric Controlを選択して、この状態でドラッグし、先ほど配置したArrayのグレーの四角部分にドロップします。
なお、制御器は結果を表示するものではなく入力を与えるものでした。上で紹介した足し算の例では結果が複数得られるという話でしたが、そうした処理の結果を扱う場合には数値の制御器ではなく表示器をArrayに渡すことになります。
一方で毎回の足し算で違う数を計算させる場合にはこのように「複数の入力」として制御器の配列を使うことになります。
これで数値の配列が用意できたことになります。ただし、このままでは実は「箱が0個の配列」となっています。文字通り、何のデータもない、状態です。
配列で箱を増やすには、具体的に箱に値を入れていく必要があります。専門的な言い方をすると配列を初期化する(Initialize Array)といい、これをしないと箱が増えません。
今見えている数値制御器の部分に適当に値を入力すると、今までグレーで薄く表示されていた配列が明るく表示されるようになるのがわかると思います。
この明るくなった状態が「初期化」されたことをあらわしています。
ところで、複数の箱があると説明した割には、今数値制御器として見えているのは1つだけです。全然、「データの集まり」という感じがしないと思います。
それに、左側に、最初から数値制御器らしきものがついているのもなんだか気になると思います。
勘のいい方はわかったかもしれませんが、この左側に見えている数値制御器らしきものは、先ほどのキーワードの一つである「指標番号」を表しています。
そしてこの指標番号のすぐ右側にある数値制御器が、その指標番号の箱に入った要素、ということになります。
つまり、今の状態は「数値配列の指標番号0に4という値が入っている」状態を指していることになります。
ここで、指標番号の部分に3と入力してみてください。すると、数値制御器(要素の部分)は再び薄い表示になります。それは、この配列の指標番号3(箱の数としては4番目)にまだ値が入っていないからです。
そこで、この状態で数値制御器に値を入れておきます。すると、やはり明るい表示になりました。
このとき、飛ばした指標番号1、2の要素はどうなっているか、というと「デフォルト値」が入っている状態になります。
操作としては最初指標番号0の要素に対応する値を入力してその後指標番号3の要素に対応する値を入力しただけなのですが、もしこの二つの指標番号に対応する要素しかなかった場合、値は2つしかないことになります。
しかし、指標番号3のところに値を定めた、ということは「4番目の箱に値を入れた」ということになるので、その状態にするためには「2番目の箱」と「3番目の箱」が存在している必要があります(2と3がないのに4番目の箱という状態はあり得ないというわけです)。
そこで、飛ばした間の2と3にはそのデータタイプのデフォルト値が自動的に入り、今回の場合は数値データタイプとしてのデフォルト値である「0」が入ります。
ちなみに、数値の配列の場合デフォルトの値は0ですが、変えることもできます(が今回は割愛します)。
この「ある指標番号にどんな値が入っているか」を調べるという操作、もちろん指標番号の表示部分を変えることで中身を確かめることもできますが、ちょっと不便です。
そんなとき、配列を横あるいは縦に引き伸ばしてその配列の複数の要素をまとめて表示させるということもでき、あまり多くを一度に表示させるのでなければ、これが便利な方法となります。
横でも縦でもどちらでも構いません。共通して「指標番号表示のすぐ右隣の値が、その指標番号に対する要素」となっています。
なお、実際には、配列は数値だけでなく、文字列やその他のデータタイプについても作ることができますし、制御器や表示器それぞれで作成できます。
ただし、「制御器と表示器が混在した配列」や「配列の配列」はできません。
ここ、他のプログラミング言語(例えばPythonなど)を知っている方にとっては違いが出るポイントだと思います。Pythonではリストが配列に近いものだと思いますが、「リストをリストの中に入れる」といった構造を取れるものの、LabVIEWではこれができません。
ただし、LabVIEWとしてはこの問題は配列の次元を変更することで対応できます。これには、指標番号表示を下に引き伸ばすだけです。
配列の次元というのは、ざっくり言うと配列の配列を表現しているのに近い状態です。
要は、あるデータタイプの要素が複数並んでいる状態が作れればいいので、「横方向にのばした配列が縦に並んでいる」、あるいは「縦方向にのばした配列が横に並んでいる」状態を表せます。
これはなんだか表計算に似ていますよね。1次元であれば縦か横にずらっとデータが並んでいますが、2次元であれば縦にも横にもデータが並んでいる状態となります。
もし(1次元の配列を)縦に伸ばしていた場合、これが横にも伸びるとなると「『縦に伸びた配列』が複数ある」と言えるので、配列の配列と言えなくもない、ということになります。
次元は3次元、4次元、5次元、・・・と大きくすることができますが、一般的なプログラムでは大きくても3次元配列までしか使わず、4次元配列以上はほぼ使わないと思います。
3次元、というのは表計算ソフトでいうところの「シート」に対応するようなイメージをもてば分かりやすいと思います。
本シリーズでは3次以上の高次元の配列は扱わないのでこれ以上は説明をしません。
ひとまず、複数のデータを様々な形でまとめて保持できるデータタイプ、それが配列だということを覚えてください。
配列に関して最後に覚えておくべきは、ブロックダイアグラム上の扱いになります。
LabVIEWはデータタイプを色で分けたように、配列の状態(次元)も視覚的に表現しています。具体的には、配列の次元が大きくなるほど、ワイヤが太くなります。
配列ではない、単体の値の状態を、配列と区別するためにスカラ(scalar)ということがありますが、スカラは細い線であるのに対し、配列にすると次元が上がるにつれて太くなります。
実際に色々なデータタイプで制御器の配列と表示器の配列を作り、ワイヤで結んで確かめてみてください。
繰り返しを行うための仕組み:WhileループとForループ
さて、準備段階のわりに長々と配列の説明をしましたが、ここからいよいよ処理を繰り返すという方法について紹介していきます。
繰り返しを行う際に考える必要があるのは、どのように繰り返しを止めるか、です。
LabVIEWで繰り返しを行うための仕組みは二つあるのですが、その二つは繰り返しを止める方法がそれぞれ異なります。
他のプログラミング言語を知っている方には割とイメージがつきやすいと思いますが、
- 処理を終えて、繰り返しを止める条件が成立していなかったら次の繰り返しを行うWhileループ
- あらかじめ繰り返しの上限を決めておいて、上限に達したら繰り返しをやめるForループ
の二つがあります。
これら二つの仕組みについて、共通する考え方と、異なっている部分をそれぞれ見ていきます。
まず共通点ですが、以下の二つがあります。
- 繰り返しの構造の外部と内部をつなぐ「入力」と「出力」がある(なくてもよい)
- 繰り返しの構造の中身すべてが終わってからでないと次の回に進まない
基本的なこととして、LabVIEWでは繰り返しの構造を「囲み」で表します。具体的には以下のようです。
この囲みの中身に、繰り返したい処理の内容を書いていきます。
逆に言えば、こういった囲みの外にある処理は一回実行されれば終わりです。
繰り返したいときには必ずWhileループあるいはForループの囲みの中に入れるようにします。
(ここから先で紹介しているいくつかの例は、二つのループに共通する仕組みのイメージを説明するもので、実際には「足りない」ものがあるためプログラムとしては実行できないことに注意)
これら繰り返しの構造(囲み)には、外から値を入力することができます。
例えば、以下はWhileループを使用した例ですが、外からxという名前で与えられた数値制御器、Numeric controlの値を使って常に計算を行っている、ということになります。
囲みの中にはyと書かれたもう一つのNumeric controlがあり、yの値は毎回の繰り返し毎に値の更新を反映させることができますが、最初の、つまりWhileループの外にあったxの値は毎回同じ値が使われます。
「いやいや、最初のNumeric controlの値も毎回変えたいよ」という場合には、これもWhileループの中に含めないといけません。
これらの事情はForループでも基本的には同じです。ただ少しだけデフォルト設定に違いが出てきたりする部分なので、その点はもう少し後で紹介します。
次に、繰り返しの構造の中身すべてが終わらないと次の回に進まないという話ですが、これは考えたら当たり前の話で、WhileループやForループの中にある処理で「一部は一回しか実行されない」部分はない、ということになります(次回の記事で紹介する条件分岐を使えばそのように組むこと自体はできますが)。
前回の記事で紹介したように、LabVIEWプログラムはある処理に対して入力が揃ったら出力を行う、という絶対的なルールがありました。
上の右側の例だと、二つの計算式、(x+y)×zと(p-q)/rは、片方の計算の結果をもう片方の計算の一部(入力)としているわけではないため、(x+y)×zと(p-q)/rどちらが先にどのような順番で実行されるかはわかりません。
(これらは並列で実行されるので、片方の計算が終わってからもう片方の計算が始まる、というものではないのに注意)
ここで決まっているのは、あくまで「(x+y)×zと(p-q)/rの二つが実行され終えたらWhileループの1回が終了する」、ということです。
なぜこんな話をわざわざしているかというと、繰り返しの構造の中でもし何か「プログラムの流れを滞らせるような処理」があった場合に、繰り返しが意図したタイミング通りに実行されなかったり、最悪繰り返しを止めることができずにプログラム自体も終わらせられなくなるという事態に陥る可能性があるためです。
時間がかかるような処理というのは確かに存在します。例えば、ファイルにデータを保存するといったものですね。そういった処理があっても繰り返しをなるべく意図したタイミングで実行させるようなプログラムの書き方もあります。
ただし、このような書き方は少し上級な組み方になるので、本シリーズでは扱いません。もし気になるという方は、「まずこれ」のシリーズの第23回以降で扱っていますので見てみて下さい(この回だけ見ても知らないことだらけ、となると思うので今の時点で初心者の方が見るのはオススメしませんが)。
色々書きましたが、繰り返しの構造であるWhileループとForループに共通した仕組み二つはまず何となくでも頭に入れておいてください。
次に、WhileループとForループとで異なる点を見ていきます。
- Whileループは「条件」で繰り返しを止めるかを決めるのに対してForループは「回数」で決める
- Whileループ、Forループそれぞれの配列の「入力」と「出力」のさせ方のデフォルト設定が違う
まず、WhileループとForループとで繰り返しを止める方法が異なるという点について、これが両ループの一番大きな違いになります。
Forループがわかりやすいのでまず先にForループの方を例に出しますが、こちらはForループの左上にある「N」と書かれた部分に入力した数値が「Forループを繰り返す回数」となります。
なので例えば以下のForループは合計で5回回ることになります。
これを確かめるために簡単なプログラムを作ってみます。
Forループの作り方はいたって簡単で、ブロックダイアグラム上で右クリックして関数パレットを表示させ、StructuresのカテゴリにあるFor Loopを選択、あとは枠組みの大きさを決めるためにドラッグアンドドロップの要領で四角の枠の大きさを決めます。
「i」と書かれた部分は反復端子(iteration terminal)といって、「今繰り返しの何回目か」という数値を取得するために使用することができます。
ただし、これも配列の指標番号同様0から数え始めます。なので、以下のプログラムを実行すると、Numeric indicatorである数値表示器には「4」と表示されるはずです。
え、プログラムを実行したら一瞬で終わったので、繰り返されたかわからない?それはなぜかというと、繰り返しを行うタイミングを指定していないからです。
プログラムとしては、繰り返しを行う際の「繰り返しの周期」をもちゃんと指定する必要があるのですが、それがどこにも指定されていない場合には「とにかく早く」繰り返します。これはCPUの性能に依るようで、要はPCによってまちまちの早さになります。
ただし、「とにかく早く」は必ずしもいいこととは言えません。むしろ、わざわざ繰り返しの周期を制限しなくてはいけない場合の方が多いです。
繰り返しのタイミングを決めるために今回は待機(Wait (ms))の関数を使います。下の図のようにWait関数を繰り返しの中において、ここに対して定数を作成(Create constant)をし、値として500を入力してください。なおこの指定の単位は「ミリ秒」です。
待機の関数は、以下の図の場合Forループの実行について毎回「500ミリ秒待つ」という処理を行うと思ってください。
今、「i」となっている反復端子と待機関数には何の依存関係もないので、これらはそれぞれ並列で実行されると考えると、反復端子から「ループの何回目」という値が出る(という処理)のは一瞬なのに対して、待機関数は500ミリ秒待つということなので、結果的にループのある一回は500ミリ秒経たないと次に進まないということになります。
なので、この状態で先ほどのプログラムをもう一度実行すると、確かに500ミリ秒ごとに値が0から1、2、3、4と変わることがわかると思います。
次にWhileループの方ですが、こちらは「条件」で止めるという説明をしました。
ここでいう条件とは、例えば「処理した結果が3以上になったら」とか「ユーザーがボタンを押したら」とか、「エラーが起きたら」、といったような「あらかじめいつそうなるかわからない」条件となることが多いです。
なお、他のプログラミング言語との比較で気になる方もいると思うので補足すると、LabVIEWのWhileループは停止の条件判定によって繰り返しを終了すると決定しても、それを決定した回はループの中の処理を実行します(感覚としてはwhile ~ do ~ではなくdo ~ while~ということです)。
そのため、条件端子に最初からTrueが配線されていたとしても中身の処理は絶対に一回は行われることになります。
こちらもまた簡単な例で処理を確認してみます。
Whileループを表示させたら、右下に表れる赤いポチを右クリックします。この赤い丸ポチは条件端子(Conditional terminal)と呼ばれ、この部分に条件が達成されたかという情報を入力します。
とりあえず今は、この条件端子を右クリックして、制御器を作成(Create Control)します。
すると、Stopと書かれた緑色の「化身」が出てきたかと思います。この緑色はLabVIEWではブールデータを扱うときの色です。
いきなり出てきたブールデータって何?ということについては、次回の記事で扱います。
今はとりあえず、「条件判定を行うためのデータ」と思ってください。
このプログラムは「ユーザーがフロントパネル上でStopのボタンを押したら終了する」、という状態になっています。
せっかくなのでForループでもやっていたように反復端子に表示器を付けてプログラムを実行してみてください。
とても高速で反復端子の値が変わっているのがわかると思います。つまり、それだけループを繰り返しているということになります。
もちろん、ループの一回の速度を制限するのに、Forループ同様、Whileループでも待機(Wait (ms))の関数が使用できます。
ブールについての紹介はまだしていないので詳しい説明はしにくいのですが、別に制御器だけしか使えないわけでもないので、Whileループを止める他のプログラム例も少し紹介します。
さて、これでWhileループとForループの一番大きな違いを説明しました。
「ところで配列はどうなったの?」とそろそろ突っ込まれそうですが、ここからの話でようやく配列が出てきます。
WhileループとForループとで配列を「入力」あるいは「出力」させるときのデフォルト設定が異なる、という特徴について説明するために、以下の二つのプログラムを作ってみます。
簡単に、作り方の手順を紹介します。なお、以下に紹介する順番は一例で、この順番でなくてはならない、ということはありません。
まずは、WhileループとForループを配置して、足し算をするためにAddの関数を両方のループの中に入れておきます。
ついでにWhileループの条件端子を右クリックしてCreate ControlでStopの制御器も作っておきます。
待機の関数も付けておきましょう。
次に、ループの外に数値定数を置いて、右クリックでChange to Arrayとすると数値の配列の定数ができます。これは、上で紹介した配列の作り方とは異なりますが、より簡便に制御器/表示器を配列にするのに役立つ方法です。
これに値を入れて初期化したら、ループの外からWhileループおよびForループの中に置いたAddにそれぞれ配線します。
そして、Addのもう一つの入力として反復端子も配線しておきます。
ここまで終えたら、Whileループの中のAddの右側で右クリックしてCreate Indicatorをします。すると、数値の配列の表示器が現れます。
では次にForループの中のAddに対しても同じくCreate Indicatorをします。すると、配・・・ではなくスカラーの数値表示器が現れます。これが正しいです。
プログラムを作り終えたら適宜形を整えて実行してみます。
Whileループはユーザーが止めるため手が加わりますが、ともかく両方を実行すると結果が違うことがわかります。(片や配列、片や数値表示器で結果が出ているというのがまずそもそも大きく違うのですが)
一見、どちらも処理内容は足し算をしているという意味で同じなのですが、何が異なるのか?
それは入力(出力)時の配列の扱い方です。
- Whileループは配列を「そのまま」入力するのがデフォルト指定
- Forループは配列を「次元を減らして」入力するのがデフォルト設定
どういうことか、わかりやすいのはWhileループだと思います。
Whileループではループの外から入力された配列はWhileループの中でも同じ配列としてそのまま扱われるので、元々の(ループの外にあった)配列が、2ずつ異なる値が4つ入った配列であったために、反復端子の値を足していることで、値が変わっているという状態になっています。
なお、このように配列に対して単一の値(今回で言えば反復端子の値)を足す場合、その足し算は配列の全要素に対して行われます。これは足し算だけでなく引き算や掛け算、割り算も同様です。
下の図で赤枠で囲った部分を入力トンネルと言いますが、Whileループは入力トンネルとして配列をそのまま受け付けます。
一方Forループの場合にはトンネルに入る時点で次元を一つ落としたような入力の仕方をします。
今回は1次元配列なので、これの次元を落とすとスカラー(単一の)値となります。
繰り返しの1回目には元の配列の要素0の値が使われ、繰り返しの2回目にはその配列の要素1が使われ、といった、入力された配列の要素を順番に入力値として受け入れるということです。
その証拠に、よく見るとForループの中でのワイヤの太さが、Forループの外よりも細くなっているのがわかります。
これに伴いAddの出力に対して作成した表示器もWhileループでは配列、Forループでは単なる数値表示器になっていました。
Forループで、外から配線した配列(定数じゃなくても制御器でも構いません)がループの枠組みの中で配列ではなくなっている状態を、自動指標付け(Auto Index)と表現します。
なので、Forループは「自動指標付けを有効(Enable Auto Indexing)」がデフォルトの設定となっています。
この方法を使うと、実はForループの回数を決める必要がありません(「N」に数値を入力しなくても問題ありません)。上の図がそうでした。
なぜなら、このような入力をした場合にはForループは入力された配列の要素の数だけ回ったら自動的に終了するようになるためです。
なお、「次元を減らして」入力と表現したように、もし入力する配列が2次元の場合、各繰り返しでは1次元配列が入力トンネルから使われることになります。
ただし、「デフォルト設定が違う」というだけで、例えばWhileループでも配列の要素をそれぞれ取り出すようにすることもできますし、Forループで入力された配列をその配列のまま扱うこともできます。
これらの違いは、入力時のワイヤがWhileループと交わった部分の四角の見た目で判断でき、「自動指標付け」(Indexing)の機能の有無として設定できます(もちろん、Forループに対してこの自動指標付けを無効にした場合には、「N」の部分に数値を入力する必要があります)。
これは出力に対しても同じことが言えて、
- Whileループは繰り返しの最後の値だけ出力する
- Forループは繰り返しの毎回の値が出力配列の各要素となって出力される
ような設定がデフォルトになっているのですが、設定をもう片方のループのやり方に切り替えることができます。
特にこの指標付けの設定の有無はForループでとてもよく使います。
さて、これで繰り返し構造の仕組みについては一通り説明したのですが、最後にもう一つだけ重要な仕組みであるシフトレジスタの紹介をして今回は終わろうと思います。
このシフトレジスタとは、「一つ前の繰り返し回の値を取得する」機能になっています。
例として、以下のようなプログラムを考えてみます。
このプログラムを実行すると、WhileループのNumeric controlには、0、1、3、6、10、・・・と順番に表示されていくのがわかると思います。
これは、「前の回の足し算の実行結果に、今の回の反復端子の値を足している」という処理を繰り返している状態になっているためです。
シフトレジスタはWhileループやForループの枠組みに対して作成することができます。
例えば以下のようにループの枠組みの外から何か値を入力しているときに、入力トンネルの部分を右クリックしてシフトレジスタに置換(Replace with Shift Register)すると作成できます。
一つ前の繰り返しの値を使って現在の回で特定の処理を行う、ということを繰り返すのは割と頻繁に行うことになります。
そして、LabVIEWでプログラムを書けるようになるためにはこのシフトレジスタの機能は非常に大事です。
このシフトレジスタという機能はWhileループ、Forループどちらでも使用できますが、ループが終わった後に得られる結果は、そのシフトレジスタに渡された最後の値であり、Forループのデフォルト設定のような「毎回の結果が配列の各要素になる」といった動作とはならないことに注意します。
「一つ前の繰り返しの値ではなく、二つ前の繰り返しの値を使うにはどうすればいいの?」といった声も聞こえてきそうですが、本シリーズでは割愛します。(気になる方は以下の記事を見てみてください。)
だいぶボリュームがありましたが、配列および処理を繰り返すという仕組みの説明はここまでにします。
次回は、プログラミングをする上で、繰り返しと同じくらい重要な条件分岐の仕組みについて紹介していきます。
よければ次の記事もみていってください。
ここまで読んでいただきありがとうございました。
コメント