Mr.Hack | ||
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ◆目次◆ ■Question13の解答 皆さん、こんにちは。 Mr.Hackです。 いやー、日本は暑いですねー。この間、2週間ほど、日本に帰国していたのですが、梅雨は梅雨でむしむし、台風直撃、梅雨が明けると、とたんに30度を超える猛暑と、なかなか手強いですね。おかげで、体を日本の気候にあわせるのが精一杯でした。今回は、手見上げに、PCカードを二回り大きくしたぐらいの無線プリンターサーバーを買って帰りました。これは、メルコからでているのですが、思わず一目惚れ、衝動買いをしてしまいました。とーっても、ちっちゃくていいと思いますよ。メルコのプリンターサーバ。LANを組んでいれば、普通の人はたいていプリンターはルータのそばに置くでしょうから、わざわざ無線にする必要はないでしょうね・・・。僕の家では、ルーター兼無線アクセスポイントとプリンターが寝室に、パソコンは普段別の部屋でやるので、プリントするたびにわざわざ寝室にいかなければならず不便だったのです。今度は、プリンターはすぐそばの場所におけそうです。プリンターの配置に困っている方は、無線プリンターサーバーはいい選択肢だと思います。 さて、前回は、ド・モルガンの法則を勉強しました。これを習得することによって、if文のような条件節に強くなれるということをお話ししました。それでは、早速宿題をみてみましょう。 ■Question13の解答 ある文字(キャラクタ、character)が数字であることをチェックするには、 ┌──────────────────────────────────┐ でした。同じように、characterが大文字であることをチェックするには、 ┌──────────────────────────────────┐ で、小文字であることをチェックするには、 ┌──────────────────────────────────┐ です。characterがこのうちのいずれかであればいいので、||を使って ┌──────────────────────────────────┐ となります。 今度は、”『数字か大文字か小文字』ではない”場合は、falseを返すようにした2.をみてみましょう。 ┌──────────────────────────────────┐ 先ほど、1.でelse節にあった条件を、if節に持ってきてあげればいいわけですが、elseの条件は、if節の条件の補集合に当たりますので、 ┌──────────────────────────────────┐ が成り立ちます。つまり、if節の条件を否定してあげればいいですね。 ┌──────────────────────────────────┐ ここで、ド・モルガンの法則を思い出してください。 ┌──────────────────────────────────┐ でしたね。(A OR B)の全体を否定すると、それぞれの部分を否定し、ORがAND(ANDの場合はORに)なりましたね。このド・モルガンの法則を適用すると、 ┌──────────────────────────────────┐ のそれぞれの部分、(character >= '0' && character <= '9') 、(character >= 'A' && character <= 'Z')、(character >= 'a' && character <= 'z')) を否定し、||を&&に変えてあげればいいですね。(character >= '0' && character <= '9') を否定するということは、補集合をとればいいですから、数字ではないキャラクタ、つまり、(character < '0' || character > '9')になります。実は、ここもド・モルガンの法則を適用しているのがわかりますか? ┌──────────────────────────────────┐ となります。同じように、(character >= 'A' && character
<= 'Z')、(character >= 'a' && character <= 'z')) にド・モルガンの法則を適用しそれぞれ、 を得ます。故に、 ┌──────────────────────────────────┐ となります。どうです?機械的に記号を変えていくだけなので、簡単でしょう?また、機械的にできるので、正確に答えを導き出すことができます。 宿題でウォーミングアップができたところで、Question 11に戻ってみましょう。Question 11はBankアカウントを作るときに、適切なIDとPasswordを要求するように設定する問題でした。もう一度みてみましょう。 ■Question 11のNo.2 2. アカウントIDの仕様は以下の通り (a) アカウントIDはアルファベットの大文字か小文字のみ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ (a)からしなければならないことは、ある文字列(String)の一つ一つのキャラクタが、アルファベットの大文字か、小文字でなければなりませんね。これは、Question 13で使ったような方法を使います。 (b)は、3文字以上のStringから構成されなければならないので、String#lengh()メソッドを使えば文字数をチェックできますね。 ユーザーが作成しようとしているIDをチェックするメソッド、checkAccountId()をBankAccountクラスに作りましょう。メソッドの定義は、 ┌──────────────────────────────────┐ として、accountIdが(a)、(b)の条件を満たしていればtrueを、満たしていなければfalseを返すようなメソッドです。このメソッドを作ると何が便利かといいますと、BankAccountのインスタンスを作るとき(初期化時)に、accountIdとして渡されたStringが適切かどうかチェックできますね。適切でなければ、RantimeExcpetionのサブクラスである、IllegalArgumentExceptionをスローできますね(この辺がわからない方は、vol.007を参照)。あるいは、setAccountId()メソッドを作った時も、そのメソッドの中で、checkAccountId()メソッドの戻り値booleanをチェックして、同じように、IllegalArgumentExceptionをスローできますね。 それでは、checkAccountId()メソッドの中はどのようなことをしなければならないでしょうか? ┌──────────────────────────────────┐ ある文字列(String型)が適切な文字列かどうかをチェックするときは、かならず『nullかどうか』をチェックしたいですね。というのも、String型は参照型ですので、オブジェクト(メモリーアドレス)を指していない状態(null)が存在します。そのnullであるString型の参照変数をcheckAccountId()メソッドに引数として渡したときに、なにかString型のメソッド(たとえば、length()メソッド)を適用すると、NullPointerExceptionが発生します。これは、RuntimeExceptionのサブクラスです。つまり、コンパイル時にはエラーをチェックされないため、見落とす可能性がありますね。参照型のメソッドを引数として使うときは、nullの場合も考えましょう。 nullでなければ、次は、空文字であるかどうかを、equals()メソッドやlength()メソッドを使ってチェックできますね。もし、nullや空文字の場合は、accountIdとして適さないので、falseを返します。 ┌──────────────────────────────────┐
┌──────────────────────────────────┐ です。もう簡単ですね。 さて、この文をクリアーしたaccounIdはnullでも空文字でもない文字列です。 (b)の3文字以上からなる文字列はどうしましょうか?そうです。同じように、length()メソッドをつかって accountId.length() < 3 というようにチェックできますね。 ┌──────────────────────────────────┐ ここで、お気づきの方もいるかと思います。空文字が文字列の長さがゼロなので、3文字未満の文字列も同様にfalseを返すようにすればいいので、まとめて、 とできますね。accountId == 0 という条件は、accountId.length() < 3に含まれます。 さて、(a)はどうしましょうか?キャラクタのチェックは、一文字一文字づつしかチェックできないので、文字列全体をチェックする場合(たとえば、Mr.Hackという文字列)は、ループを使って文字を一つずつチェックしなければなりません。String型の実態は、char型の配列だということを以前お話ししました。そのため、各配列にアクセスできるString#charAt(int index)メソッドがあります。これを使えば配列の各indexに対応する文字を得ることができますのでfor ループ等でi (index)を一つずつ進めてあげれば良さそうです。 ┌──────────────────────────────────┐ //でcharacterをチェックしたいわけですが、どうチェックすればいいでしょう?文字が正しい場合にtrueを返す方がいいですか?それとも、正しくない場合にfalseを返すようにした方がいいですか? ここには、すごく重要な考えが潜んでいます。 ■『全て』がアルファベットの大文字か小文字の証明は大変な労力が必要 String型であるaccountIdの一つ一つの文字character全てがアルファベットの大文字か小文字で証明するためには、大変な労力がいります。言い換えますと、accountIdが全てアルファベットの大文字か小文字である文字列で、trueを返すようにプログラムを書きますと、大変な労力がかかります。もし、accountIdが1000桁からなる文字列だとすると、この文字列が全てアルファベットの大文字か小文字でなければなりませんから、全てのindexの文字を調べて大文字・小文字か否かを確認しなければなりません。たとえば、for ループを使って ┌──────────────────────────────────┐ boolean isUpperOrLower = true; //フラグをセット if (isUpperOrLower) { └──────────────────────────────────┘ 全ての文字のチェックが終わってフラグであるisUpperOrLowerがfalseとセットされなければ、accountIdは適切な文字列だと証明できますね。しかしながら、その証明には、1000桁あれば必ず1000桁分ループしなければなりません。これは、桁が増えれば増えるだけ大変なコストがかかります。 しかしながら、逆に、accountIdが不適切な文字列だと証明するためには、最短でループは1回(例えば、"@Mrhack.....")、多くて1000回(例えば、"....mrhack&")とばらつきがでます。というのも、1000桁あるうちのどれか一つだけでも、アルファベットの大文字か小文字でない文字が含まれていることを証明できれば、その全体のaccountIdは不適切な文字列だと証明できるからです。 つまり、accountIdが適切な文字列であること(すべの文字がアルファベットの大文字か小文字であること)を証明することよりも、accountIdが不適切であることを証明する方が労力がかからないのです。 アルゴリズムのコストの観点から考えれば、for ループの中でチェックしなければならないのは、『アルファベットの大文字か小文字でない』文字ということになりますね。その場合に、return falseを返すようにすれば、全ての文字をチェックしなくても、accountIdが不適切な文字ということを証明できます。コードはこんな感じになります。 ┌──────────────────────────────────┐ アルファベットの大文字か小文字の場合は、 ┌──────────────────────────────────┐ ですので、それでは『ない』場合は、否定をとってド・モルガンの法則を適用すればいいので、 ┌──────────────────────────────────┐ となります。 以上をふまえて、checkAccountId()メソッドは下記のようになります。 ■checkAccountId() メソッド 今回は、ド・モルガンの法則を応用して、アカウントIDがアルファベットの大文字・小文字で、三文字以上であるという条件をcheckAccountId()というメソッドで作成しました。また、”全てが正しい”(文字列がアルファベットの大文字か小文字であること)ということを言う(証明する)ことは、非常に労力がかかることを勉強しました。逆に、一つでも『正しくない』(アルファベット以外の文字が文字列に含まれる)ことがいえれば”全てが正しい”ということが”正しくない”と証明できることも学びました。少し、難しい内容になりましたが、プログラムを書く上で大事なことですので、復習しておいてください。 それでは、また、あいましょう。 Mr.Hack
|
||
© 2002 MR.HACK ALL RIGHTS RESERVED |