Q22:Lesson13 拡張電卓版Aについて。2つの算術演算子コンポーネント格納変数の役割の違いというか加算コンポーネントとの役割の違いとかがイメージできず難しいです。
Q22-1:
電卓拡張版Aを理解しようとしているのですが算術演算子コンポーネント格納変数が悩ましいです。数字を選択し、「+」ボタンを押したとすると、その後の処理はまず、加算コンポーネントを算術演算子コンポーネント格納変数内に入れています。そしてイベントが発生するために、左の入力演算子格納変数に進み、ID23のサブルーチンに飛びます。そこで内部演算子格納変数のNULLチェックを行い、NULLなら内部演算子格納変数の中身を初期化し、ついでに文字列格納変数にも「0」を入れて、入力演算子格納変数に入っている演算子を内部演算子格納変数に入れています。
もし、内部演算子格納変数の中身があれば、NULL判定から発生するイベントに飛んで、任意精度実数格納変数の数値(要は「+」を押す前に表示されていた数字)を取得し、それを内部演算子格納変数に入れて演算しています。この時の内部演算子格納変数内での処理のイメージがどうも上手く掴めません。内部演算子格納変数内に入っている加算コンポーネントに任意精度実数格納変数の数値を右オペランドに設定してから演算しているのでしょうか?
2つの算術演算子コンポーネント格納変数の役割の違いというか加算コンポーネントとの役割の違いとかがイメージできず難しいです。
A22-1:
Lesson.13の拡張版電卓は確かに難解だと思います。
> 数字を選択し、「+」ボタンを押したとすると、その後の処理はまず、加算コンポーネントを算術演算子コンポーネント格納変数内に入れています。そしてイベントが発生するために、左の入力演算子格納変数に進み、ID23のサブルーチンに飛びます。そこで内部演算子格納変数のNULLチェックを行い、NULLなら内部演算子格納変数の中身を初期化し、ついでに文字列格納変数にも「0」を入れて、入力演算子格納変数に入っている演算子を内部演算子格納変数に入れています。
ここは少し違います。NULLの場合にはNULL判定から発生するイベントの接続先の処理が行われないだけで、内部演算子格納変数および文字列格納変数の初期化処理は、内部演算子格納変数の中身がNULLのときもそうでないときも実行されます。NULLでない場合には、これらの初期化処理が、NULL判定の先の処理が行われた後に実行されるということです。
> もし、内部演算子格納変数の中身があれば、NULL判定から発生するイベントに飛んで、任意精度実数格納変数の数値(要は「+」を押す前に表示されていた数字)を取得し、それを内部演算子格納変数に入れて演算しています。この時の内部演算子格納変数内での処理のイメージがどうも上手く掴めません。内部演算子格納変数内に入っている加算コンポーネントに任意精度実数格納変数の数値を右オペランドに設定してから演算しているのでしょうか?
その通りです。このアプリケーションでは、四則演算を切り替えるために算術演算子コンポーネント格納変数を利用しています。四則演算の各コンポーネントは、行う演算は足し算、引き算、掛け算、割り算と異なるものの、「左右オペランドの設定」と「演算の実行」というメソッドの名称は共通です。そこで、算術演算子コンポーネント格納変数を使い、押した演算子ボタン(「+」、「-」、「×」、「÷」)によって格納する演算コンポーネントを切り替えるようにすれば、同じ処理で四則演算に対応できる、というのが基本的な考えです。
> 2つの算術演算子コンポーネント格納変数の役割の違いというか加算コンポーネントとの役割の違いとかがイメージできず難しいです。
2つの算術演算子コンポーネント格納変数の役割の違いは以下の通りです。
内部演算子格納変数: 実際に計算を実行
入力演算子格納変数: 次に計算を実行する演算子を一時的に保管
"3×2-1="と入力した場合を例にとって、それぞれの算術演算子コンポーネント格納変数の中身をたどってみると以下のようになります。
(1) 初期状態
内部演算子格納変数の中身: NULL
入力演算子格納変数の中身: 不定(前に設定されたものが残っている)
(2) "3"を入力したとき
内部数値格納変数の中身: 3
内部演算子格納変数の中身: NULL
入力演算子格納変数の中身: 不定(前に設定されたものが残っている)
(3) "×"を入力したとき
内部数値格納変数の中身: 3
内部演算子格納変数の中身: 乗算、左オペランド3、右オペランド 不定(前に設定されたものが残っている)
入力演算子格納変数の中身: 乗算
(4) "2"を入力したとき
内部数値格納変数の中身: 2
内部演算子格納変数の中身: 乗算、左オペランド3、右オペランド0
入力演算子格納変数の中身: 乗算
(5) "-"を入力したとき
(5)-1 入力演算子格納変数に減算コンポーネントを設定(一時的に保管)
(5)-2 内部数値格納変数に保持されている「2」を内部演算子格納変数の右オペランドに設定して演算(掛け算)を実行(3×2が実行される)
(5)-3 内部演算子格納変数と入力文字列格納変数を初期化
(5)-4 入力演算子格納変数に保管されていた減算コンポーネントを、内部演算子格納変数に設定
(5)-5 内部演算子格納変数の左オペランドに掛け算の結果を設定(すなわち、減算コンポーネントの左オペランドに6が設定される)
この時点で各変数コンポーネントの中身は以下のようになります。
内部数値格納変数の中身: 6(3×2の計算結果)
内部演算子格納変数の中身: 減算、左オペランド6、右オペランド0
入力演算子格納変数の中身: 減算
(6) "1"を入力したとき
内部数値格納変数の中身: 1
内部演算子格納変数の中身: 減算、左オペランド6、右オペランド0
入力演算子格納変数の中身: 減算
(7) "="を入力したとき
(7)-1 内部数値格納変数に保持されている「1」を内部演算子格納変数の右オペランドに設定して演算(引き算)を実行(6-1が実行される)
(7)-2 内部演算子格納変数と入力文字列格納変数を初期化
ところで、以前に「コンポーネント格納変数を使用した場合、動作を追跡しづらくなる場合がある」と申し上げましたが、これは算術演算子コンポーネント格納変数にも当てはまります。算術演算子コンポーネントの「演算を実行する()」メソッドを実行すると、そのときに格納されているコンポーネント(例えば加算コンポーネント)の「演算を行う()」メソッドが起動されます。各四則演算コンポーネントは、「演算を行う()」メソッドを実行すると処理完了イベントを発生しますので、そのときに格納されていたコンポーネントに処理が移ることになります。
すなわち、ビルダー画面上右側にある内部演算子格納変数の「演算を実行する()」を実行すると、次の処理は、画面左側の加算コンポーネント等に移るという状況が発生します。そのため、処理の流れを追いにくくなりますので、コンポーネント格納変数や算術演算子コンポーネント格納変数を利用する場合には、注意が必要です。
Q22-2:
NULL判定からの処理に移るには内部演算子格納変数の中身が必要だと思いますが、そもそも最初にNULL設定してしまってますし、中身が入る時が存在しない気がします。
…と思いましたが、ひょっとして、最初にNULL判定にて内部演算子格納変数の中身を初期化したあと、入力演算子格納変数に演算子を設定している部分の処理がNULL判定から繋がっているイベント番号での区分になっているという理解で良いのでしょうか?
つまり、内部演算子格納変数の中身を処理した後に、四則演算の演算子がある時点で、内部数値格納変数に入っている数値を右オペランドに設定しているという感じならスッと理解できそうです。今までは内部演算子格納変数に数値があるかどうかを判定していると勘違いしていました^^;
これ以降の複合等の処理は理解できましたので、とりあえず実践編はなんとか完了しました。
A22-2:
お問い合わせありがとうございます。
> NULL判定からの処理に移るには内部演算子格納変数の中身が必要だと思いますが、そもそも最初にNULL設定してしまっていますし、中身が入る時が存在しない気がします。
> …と思いましたが、ひょっとして、最初にNULL判定にて内部演算子格納変数の中身を初期化したあと、入力演算子格納変数に演算子を設定している部分の処理がNULL判定から繋がっているイベント番号での区分になっているという理解で良いのでしょうか?
>
> つまり、内部演算子格納変数の中身を処理した後に、四則演算の演算子がある時点で、内部数値格納変数に入っている数値を右オペランドに設定しているという感じならスッと理解できそうです。今までは内部演算子格納変数に数値があるかどうかを判定していると勘違いしていました^^;
おっしゃるとおり、NULL判定で判定しているのは、演算子コンポーネント(加算、減算、乗算、除算)があるかどうかです。内部演算子格納変数にこれらの演算子コンポーネントが設定されるのは、入力演算子コンポーネントが発生するデータ設定イベントに接続された、内部演算子格納変数の「演算子を設定する(PFArithmeticOperator)」メソッドを実行したときです。
演算ボタン(「+」、「-」、「×」、「÷」)を押したときの処理の流れは以下の通りです。
(1) 入力演算子格納変数に、該当する演算子コンポーネントを設定。
(2) 入力演算子コンポーネントからデータ設定イベントが発生。
(3) サブルーチン[ID:22 KEY:"演算実行サブルーチン"]の処理を実行。
内部演算子格納変数に演算子が設定されていなければ初期化処理のみ実行。
内部演算子格納変数に演算子が設定されていれば、内部数値格納変数に入っている数値を演算子の右オペランドに設定して計算を実行し、計算結果を内部数値格納変数に設定。その後、初期化処理を実行。
(4) 入力演算子コンポーネントから発生しているデータ設定イベントに接続された、内部演算子格納変数の「演算子を設定する(PFArithmeticOperator)」メソッドを実行。次の計算を行う演算子が、内部演算子格納変数に設定される。
(5) 内部演算子格納変数からデータ設定イベントが発生。
(6) 内部演算子格納変数の「演算子の左側の数値を文字列で設定して数値変換する(String)」メソッドを実行。内部数値格納変数に入っている数値が、この演算子の左オペランドに設定される。
以上により、次の計算を行うための準備が整うことになります。
Q22-3:
無事流れは掴むことが出来ましたが、一か所だけ追加で質問させて下さい。
内部演算子格納変数のメソッドで「演算子の右側の数値を文字列で指定して数値変換する」とありますが、引数に用いている内部数値格納変数は中身が数値だと思います。
「演算子の右側の数値を設定する」メソッドを選択せず、文字列として読み込んだものをもう一度数値に変換している意味が分からないのですが…。
二度手間のように思えてなりません。
一応どちらも問題なく動作するようなのでなおさら不明です。
(StringとObjectの差?)
以上宜しくお願いします。
A22-3:
お問い合わせありがとうございます。
> 内部演算子格納変数のメソッドで「演算子の右側の数値を文字列で指定して数値変換する」とありますが、引数に用いている内部数値格納変数は中身が数値だと思います。
> 「演算子の右側の数値を設定する」メソッドを選択せず、文字列として読み込んだものをもう一度数値に変換している意味が分からないのですが…。
> 二度手間のように思えてなりません。
> 一応どちらも問題なく動作するようなのでなおさら不明です。
> (StringとObjectの差?)
一言で言えば、「演算子の右側の数値を文字列で指定して数値変換する(String)」メソッドの方が安全、すなわち、失敗がないからです。
ご指摘のように、これはStringとObjectの差で、「演算子の右側の数値を文字列で指定して数値変換する(String)」の場合には、与えられた文字列を数値として解釈して数値データを作成するという処理が行われます。一方、「演算子の右側の数値を設定する(Object)」の場合には、引数が数値データであることを前提として処理が行われます。そのため、例えば"1234"という文字列をこのメソッドの引数として与えてしまった場合には、この文字列に対応する数値データが作成されず、あとの計算が正常に行われないという現象が発生します。
実際にアプリケーションを作成する場合には、例えばテキストフィールドから取得した文字列を引数として与えるといった状況があり得ます。そのため、「文字列で指定する」をお使いになった方が、失敗がありません。
拡張版電卓アプリケーションの場合には、引数を内部数値格納変数から取得した数値データとしていますので、どちらのメソッドを用いても、上述の原因による動作不正は発生しません。