雑学講座 第三回

条件判断についてのTips.

ちょっと予定を変更して[条件判断]というキーワードでTips.を...

Palmの速度を、標準の33MHz[defaut]からターボモード40MHz[turbo]に切り替えるアプリケーションがあるとしましょう。(AppAとしましょうか)

このAppAは以下のような動作をさせようと作られているとします。

1.AppAが起動するときにシステムの速度をチェックして、
 
[turbo]だったら[ON]、[defaut]だったら[OFF]のボタンを選択状態にしてフォームを表示する。

 ←こんな感じで

2.[Done]のボタンが押されたら、[ON]と[OFF]のどちらが選択されているかを調べて、
 [ON]であればシステムの状態を
[turbo]に、[OFF]であれば[defaut]に設定して終了。

この動作はおなじみですね。(じゃないって? FEPToggleとかSelectTimeを使ってみてくださいな)

さて、今回の講座のキーワード[条件判断]は、上記の1と2のステップの両方に存在しますが、
第一ステップについて考えてみましょう。

1.AppAが起動するときにシステムの速度をチェックして、
 
[turbo]だったら[ON]、[defaut]だったら[OFF]のボタンを選択状態にしてフォームを表示する。

この動作をさせるにはどんな処理を行えばよいのでしょうか。
単純に考えて3通りの方法を挙げてみましょう。

システムの速度が...
 a.
[defaut]であれば[OFF]、[turbo]であれば[ON]
 b.
[defaut]であれば[OFF]、そうでなければ[ON]
 c.
[turbo]であれば[ON]、そうでなければ[OFF]

どれも同じように見えますが、微妙にではなくずいぶん異なる条件判断なのです。

具体的にコードを書いてみましょう。

まず、aから

Boolean appStatus;
UInt16 sysSpeed = GetSysSpeed ();

if (sysSpeed ==
defaut)
 appStatus = isOff;
else if (sysSpeed ==
turbo)
 appStatus = isOn;

if (appStatus == isOn) {
 setBtnIsSelected (OnBtn, isOn);
 setBtnIsSelected (OffBtn, isOff);}
else {
 setBtnIsSelected (OnBtn, isOff);
 setBtnIsSelected (OffBtn, isOn);}

こんな感じになります。
GetSysSpeed ()とsetBtnIsSelected ()は別途用意してある関数だと思っておいてください。

そうそう、if、elseというキーワードが出てきましたね。
それぞれ、「もし〜だったら」「そうじゃなかったら」と読み替えてください。

まず、sysSpeed = GetSysSpeed ()のところで、システムの速度を調べてsysSpeedという変数にその値を格納します。

次に、sysSpeedの値がdefautかどうか(等しいかどうか)を判断して、そうであればappStatusという変数にisOffという値を格納します。
もし、
defautでなかったら、今度はturboかどうかを判断して、そうであればappStatusにisOnという値を格納します。

あとは、appStatusの値を調べて、ボタンの選択状態を設定しておしまい。

さて、完璧に見えますが、大きなミスが含まれています。

もし、AppBというアプリケーションがシステムの速度を[slow]という状態にセットしてしまっていたら
appStatusの値はどうなるのでしょう?

普通はわざわざ遅くするなんて考えられませんよね。
でも、遅くするとバッテリの持ちが良くなるのですよ。

答え:判りません (この状態を[不定]と言います)

もし、slowだったら、appStatusの値はきちんと設定されないまま後半に突入してしまいますね。

実は、一番頭の

Boolean appStatus;

という一文は、「appStatusという変数を使いますよ」と言っているだけで、初期化しなさいとは言っていないのです。

つまり、上に挙げたコードは、例外の状態が絶対に無いという前提で書かれていることになります。
「システムの速度は
defautturboのどちらかでしかありえない」ということが大前提で成り立つコードなのです。
しかし、実際は、どこにもそんな保証はありません。

この場合、一番頭の

Boolean appStatus;

Boolean appStatus = isOff;

と書き換えてあげる必要があります。

さて、これで確実にappStatusの値はisOnかisOffにセットされるので完璧と思います...

が、実際は上記のbまたはcの方法を取る場合が多いので、続いて...

bをコードに直してみましょう

Boolean appStatus;
UInt16 sysSpeed = GetSysSpeed ();

if (sysSpeed ==
defaut)
 appStatus = isOff;
else
 appStatus = isOn;

if (appStatus == isOn) {
 setBtnIsSelected (OnBtn, isOn);
 以下同じ...

ではもう一度、
もし、AppBというアプリケーションがシステムの速度を
slowという状態にセットしてしまっていたら
appStatusの値はどうなるのでしょう?

答え:isOn

ありゃりゃ、Offのはずなのに...

sysSpeedがdefautではないので、appStatus = isOffを飛ばして、
次のelse以下のappStatus = isOn;でappStatusはisOnになってしまいます。

このコードも例外のslowという状態を想定していないことになります。

次のcをコードに直してみましょう。

Boolean appStatus;
UInt16 sysSpeed = GetSysSpeed ();

if (sysSpeed ==
turbo)
 appStatus = isOn;
else
 appStatus = isOff;

if (appStatus == isOn) {
 setBtnIsSelected (OnBtn, isOn);
 以下同じ...

こんどはどうでしょうか。

turboであれば、isOn
defautであれば、isOff
slowであれば、isOff

大丈夫そうでしょう?

ちょっと横道にそれますが、cのコードはこんなふうに書くこともできます。

Boolean appStatus = (GetSysSpeed () == turbo);

setBtnIsSelected (OnBtn, appStatus);
setBtnIsSelected (OffBtn, !appStatus);

たった3行で終わってしまいました。(解説はめんどくさいので省きますが)

さて、今回のケースではa、またはcの処理をしないとおかしなことが起こりうるということがお判りいただけたでしょうか?

つまり、システムの速度がdefautというのは標準的な状態、turboというのはAppAが設定する特異な状態であると言えます。
この場合、bの処理は「標準的な状態であるか否か」を判断しており、自分自身が作り出した特異な状態かどうかは見ていません。
一方、cの処理は自分自身が作り出した「特異な状態であるか否か」を見ているので、想定しない状態にも対応できるということになります。

まとめると、

自分自身が作り出す、または検出したい「特異な状態」かどうかを判断しないと、想定しない状態に対応できませんよ。

というお話でした

次こそは、OSが用意している[フォント選択]のダイアログをどうやってすげ替えるかについて解説しようとおもいます。