0 ・ワっ新くなるテクニック テパッグが ひなた先生が教えるノ : ノ、ノッつツ・ 「 int * の実体が文字列だと実装が煩雑になるというのはわかった。 だけど、 int の格納されている " 住所 " を持っているという理解をしたほうがわかり やすくはないか ? 」 0 「そうですね。ポインタという概念の理解と、それがどのように実装されるべきか というのは、まったく別の問題ですからね。理解するうえではある程度抽象化され ているほうが応用が利くのかもしれません」 ポインタの演算の巻 「ポインタの加減算というのは定義できるのか ? 」 0 「はい。それは、いろんな定義が考えられると思います。ポインタに整数を加算す ることから考えていきます。たとえば、 C / C + + であれば、 int * をインクリメントす る ( 1 を足す ) と、 sizeof ( int ) がポインタに加算されます ( 図 2 ) 。」 マ図 2 int ☆が int の配列を指しているところ → 0 i nt i nt i nt 「 sizeof ( int ) というのは int の大きさだな ? 4 バイトか ? 」 「 C / C + + の規格上 4 と決まっているわけではないですが、今どきの処理系ならばた いてい 4 でしよう」 「どうしてインクリメントすると 4 増えるんだ ? 直観に反しないか ? 」 272
第 12 回変数の命名規則 なんて使いませんし、それならメンバだけなんで "m-" というのもなあ、と思い 最後にアンダーバーだけ付けることにしました」 「つまりは、変数名から型名を取り除いて、スコープだけにしたんだ ? 」 「そうですね。結局、スコープと言っても、メンバ変数なのかそうでないのかだけ の区別になってしまいましたが」 0 0 第 0 日 「うーん ? 」 早井が不審なものを見つけたように顔をゆがめた。 「はい ? 」 「ケンイチ君の言う、メンバ変数 " ではない " 変数とは一体何なのだ ? 」 「ここでは、グローバル変数と static 変数を除きますから、ローカル変数のみです」 「仮引数はどうなる ? 」 「あっ ! ? 」 「次のプログラムを考えてみよう」 早井がノート PC に電光石火のごとく入力して見せた ( リスト 1 ) 。 マリスト 1 仮引数とローカル変数について考えるプログラム void incrementCount(int c){ c + 1 int count if ( count く 100 ) count,• count 「普通ローカル変数という場合、仮引数も含むのだけれども、ここでは説明の便宜 上、仮引数はローカル変数には含めないものとする」 229
0 デバッグが ・電 ) 、 ~ ー」くなるテクニック 「だとしても int 「 esu にとタイプする時間がもったいないと感じます。その時間があ るのならは、この部分にコメントでも加えます」 「 int 「 esu はとタイプする時間 ? それこそ、 Visual Studi0 が進化すれば、 SomeFunc と入力しただけでそれぐらい自動的に生成してくれるようになるんじ ゃないのか ? 」 「そうかもしれません。であれば、関数名から返り値の意味が推測できないときに返り 値で何かを返すのは良くないとしか言いようがないですね」 「だろうな。でも返り値で返すほうがソースコードは短くなるからできればそうし たい。そのためには、返り値で返してもそれが何なのか意味が推測できるような関 数名をつねに付けることを心がけるべき、という結論にもなる」 「そうですね。 C + + や C # の違いだけでなく、インテリセンスの仕様 1 つが、返り値 はどうあるべきかという議論に影響するんですね。まったく驚きました」 「うん。だから、 EcIipse を使っている人と Visual Studi0 を使っている人との話が 平行線を辿ったとしても、それは何ら不思議なことではないんだ。使用する言語や 開発環境によって、結論は正反対になることだって有り得る」 ひなた先生か教える を日臼 3 日 「ええ」 「さっきは、紙に印刷してデバッグすることを前提としたが、マルチディスプレイで 同時にいくつものソースファイルを表示してデバッグできるならば、紙に印刷する 必要すらないかもしれない。 その場合は、マウスをメソッドに hove 「させればメソッドの説明が出るのだから、 メソッド名から意味が推測できなくとも問題ないかもしれない。つまり、開発する 側がマルチディスプレイであるかどうかということも、この議論に影響するんだ」 早井先輩の本名の巻 「そろそろひなた先生が・・・ケ、、ケンイチ君、今日はお先に失礼させてもらうよ ! 」 0 「あら、早井君、もうお帰り ? 」 258
第 10 回 プログラミングのイディオム ( 後編 ) 早井先輩は暴走気味の巻 「やいコラ ! ケニチ ! ! 」 「ケニチ・・・ ? 」 「ケニチはお前の名前だろ、やいコラ ! 」 「ケンイチです」 「ケンイチもケニチも一緒じゃないのか ! やいコラ ! 」 「は、、はい。一緒です」 ケンイチは早くも半泣きだ。 「ケニチ君よ・・・今まで俺はろくなことを教えてやれなかった。すまなかったな」 「い、、いえ。早井先輩、急にどうしたんですか ? 」 「いや、なんでもない。今日は、ケニチ君に、いままでのお詫びを兼ねて、 ドモル ちゃんの報告でも教えてやろうと思ってな」 「あのう。それは一体なんでしよう ? 」 「ケニチ君は、大学の情報学科を卒業して、そんなことも知らないのか ? あの、ブ ール代数をやっていたら必ず出てくる大事な法則があるだろうが」 「つかぬことをお聞きしますが、ドモルちゃんの報告というのは、ひょっとして、 ド・モルガンの法則のことですか ? 」 「ああ、そうとも言うらしいけどな」 「は、、はい」 181
第 7 回 proxy object によるバグの再現 関係のなさそうなソースファイルを次々に削除していきプログラムを単純化し、シ ェイプアップする作業を繰り返したとします。 もしバグが発現しなくなれば、バックアップを利用してひとつ前の状態に戻します。 CVS などを利用すればこの作業は効率的に行えるでしよう。そうして、集合 A がバ グの発現する最小のプログラム集合にまで絞り込めれば、おのずと『バグがあると疑 わしい』集合 B も相当小さくなっていると思うのです」 「うん。それは見方を変えれば、プログラムの等価変換ね。『バグが発現する』という 条件を保ったまま、それと等価でより小さなプログラムに変形させていくのだから」 「そうです。しかし、これは、プログラムの等価変換 ( 『バグが発現する』という意味 において等価 ) なのですね。気付きもしませんでした。同じバグさえ発現すればどの ように変形することも許されるわけですから、相当大胆な変形も許されそうですね」 「ケンイチ君は、砂場で砂山を作ったことってないかな ? 」 「あります。子どものころ、よく公園の砂場で遊んでました」 「その砂山の上に木を突き刺すの。そして山のたもとから砂を少しずっ取り除いて いくの。木が倒れないように慎重に」 「あー、やりましたやりました。そういう遊び ( 図 7 ) 」 「この砂山がデバッグしようとしているプログラムだというメタファー ( 比喩 ) は どうかな ? 」 マ図 7 砂山で木を倒さずに砂を取っていく遊び 131
一 ' ー ' の絞り込み一 ひなた先生が テ / ーックが 第 3 回 なるテクニック ケンイチは、大学で情報工学を学んできたとは言え、実戦の場では無力だった。 入社 2 カ月が経過したケンイチは、社会の厳しさを身をもって実感する。 毎日が水曜日の巻 ケンイチは水曜になると胸の鼓動が高鳴った。水曜は会社に出社すると午前中にはひな た先生の授業がある。ひなた先生との 2 人っきりの授業は恋人のいないケンイチにとって、 デートさながらで、その場でひなた先生に美術品や高級毛布やダイアモンドを売りつけら れたら買ってしまいかねない。ケンイチは「毎日が水曜日だといいのに」と思った。 今日は、その水曜日。出社するとケンイチは、一目散に特別会議室へ向かった。特別会 議室の様子は、先週とあまり変わっていない。とくにホワイトボードにはひなた先生が先 週の授業で書いたものがそのままの形で残っている。ただ灰皿にタバコの吸殻だけが増え ていた。たぶん、特別会議室とは名ばかりで、休憩室にしか使われていないのだろう。 ケンイチが特別会議室に入ると、ほどなくひなた先生が現れた。 「あら相変わらず早いのね ! 」 「いえ、今来たばかりです・・・」 仮にここで 3 時間待っていたとしてもケンイチは「今来たばかり」と言っていただろう。 ケンイチはひなた先生のためなら火中の栗を拾うことだってできるのだ。 バグ追跡のためのニ分法の巻 「ケンイチ君は、もう会社に慣れたかな ? 」 「そうですね。仕事には徐々に慣れてきましたけど、思っていたより技術的なハー ドルが高くて」 0 0 0 42
第 8 回ケアレスミスはなぜ起きるのか ? 「これらの fo 「は up count ( ループ変数が増える方向にカウントしていくこと ) に なっている。これを down count ( ループ変数が減る方向にカウントしていくこ と ) に変形することはできるが、それはあまり一般的な書き方とは言えない。 なぜなら、たとえば N 回まわるループは『f0 「 (int i=O;i<N; + + i) 』のように書くだろ う。この書き方は up count である。もちろん、これを down count に書き換える ことはできるが、普通はそういう書き方をしない。きっと ffor(int i=O;i<N; + + i) 』 を一種のイディオム ( 決まり文句 ) としてプログラマが認識しているからだろう。 また、 a [ i ] のように配列にアクセスするときに、 i を down count にする人も珍しい。 これは、メモリの下位 ( アドレスの小さな番地 ) から、メモリの上位に向かってアク セスしていくほうが自然だからだとも言える。だとすれば、必要もないのに down count スタイルで書くほうが不自然で、 ( x + 1 ,y),(x-l , Y ) のような列挙順は、どこか おかしい。やはりここは、 up count スタイルにしておいたほうが間違いない」 4 近傍の列挙順序という、一見すればどう書いてもいいような問題にすらセンスが問わ れることをケンイチは今回、思い知らされた。そういう意味では、今回デバッグしたプロ グラムは単なるケアレスミスとは言えず、起こるべくして起こった " 事件 " だったと言え るかもしれない。 「人間なのだからミスは仕方ない」という姿勢と「ミスを犯すのが人間だから限りなくそ のミスが発生しないようにしよう」という姿勢とは正反対である。プログラマならば、後 者でありたいものだ・・・とケンイチはつくづく思った。 ( つづく ) 0 157 メメ
の ) し ) ! くなるテクニ ック テパッグが マ図 1 消しこむとは・・・ まず、数の表があって 2 3 4 5 6 1 0 1 1 1 2 1 3 1 4 1 5 7 8 9 2 の倍数 ( 4 , 6 , 8 , ・・・ ) を消しこむ 11 15 15 2 5 第 5 す 7 9 次に 3 の倍数 ( 6 , 9 , 12 , ・・・ ) を消しこむ 2 5 を 5 第 7 を 1•0 11 15 ひなた先生が教える 「いわゆる『エラトステネスのふるい注 1 』だな」 「ええ、そうです」 「知ってるなら最初に言わんか ! 」 「すみませんすみません」 まさか入社 10 分以内に 2 度も怒られるとは思っていなかったケンイチは心臓をぶち抜か 「まあ、いい。ケンイチ君は、このプログラムに対して、良いとか悪いとか、そう いう意見はないのかい ? 」 「素数を求めるアルゴリズムはエラトステネスのふるいを使う以外にもいろいろ方法 がありますが、ここからアルゴリズム的な変更は加えないと仮定しますと、まず気 になるのはプログラムの実行時のメモリ消費量です。ここで確保している配列が大 きすぎるので、 bo 型の配列の代わりに std : : vector<bool> を用いたいところで す。こうすれば、この配列による使用メモリはおおよそ 1 / 8 で済みます。速度はい くぶん遅くなりますが」 「うん。そうだね、ケンイチ君 ! 」 注 1 工ラトステネスのふるい 工ラトステネスは、ギリシアの博物学者。素数を見つける方法 ( 工ラトステネスの篩 ) を案出。また地球の形を球体と 考え、その全周を推定した ( 紀元前 276 ごろ ~ 195 年ごろ ) 。 12
第 1 回デバッグにおける不確定性原理 「そして、コメントのまずさが目につきます。『この数までの素数を求める』と書い てあるのに、確保している配列は、その数よりひとつ少ないです。たとえば b06 b[l OOOOO ] と宣言したのでは b [ O ] ~ b [ 99999 ] までしか有効ではありません。ま た、ループも同様です。つまり、このコメントは嘘で、「にの数 - 1 ) までの素数を求 める』と書くべきです。 その下のコメントに「その数が素数かどうかを表すフラグ』とありますが、この bo 団型配列は、素数ならば t 「 ue を取るのか f 引 se を取るのか、その判断に困ります。 常識的には、こういう書き方をしたなら『素数ならば t 「 ue 。そうでないならば false 』でしようし、このプログラムもそうなっているようですが」 「ケンイチ君は、なかなか聡明だな」 「いえ・・・それほどでも」 まさか入社して 10 分以内に 2 回怒られたあと 1 回褒められるとは思ってもいなかったので 「で、それでレビューは終わりか ? 」 「はい」 「ば、、ばかも一一一ん ! ! 」 突如顔を真っ赤にして怒る瞬間湯沸かし器のような早井。 「すみませんすみませんすみません」 反射的にケンイチはねずみのように丸くなった。まさか入社して 10 分で 2 回怒られたあ 「ケンイチ君は、一番大事なことに気付いておらん。それが何なのかデバッガを用 いて調べてみなさい」 「デバッガ・・・ ? 」 13
第 13 回 関数の返り値はどうあるべきか 「ふむ、なるほどな。ところでさっきの SomeFunc 日 esu は . Ok はやつばり良くない 気がするんだな」 「なぜですか ? 」 「だってほら、リスト 13 のようになっていたら、 if(f00. SomeFunc()==F00. SomeFunc 日 esu に Ok ) のように書く必要が出てくる。この F00. SomeFuncResult. Ok っていうの凄く冗長だと思うんだけど ? これって Visu Studi0 2005 のインテ リセンスが補ってくれるの ? 」 ▽リスト 1 3 SomeFuncResult の定義例 public class F00 { publi c enum SomeFuncResuLt Fi leNotFound FileReadError { Ok public SomeFuncResult SomeFunc() とタイプして〔ネ 5 三ネ〕を押した瞬間に F00. SomeFunc 日 esu はがイン 「はい。 テリセンスの候補として挙がりますね」 「そうなのか。それなら良いんだが。それにしても、読みにくいな、そのソースは」 「 . NET Framewo 「 k では、そういう特定のメソッドが用いるような構造体でも、ク ラスの外に出してしまうことが多いようですね」 「うーん、それはオブジェクト指向のクラス設計としてはややおかしい気もするが、 まあ、ソースが読みにくくなるのを避けるためなのかもしれんな」 「ええ」 インテリセンスと返り値の巻 「そろそろ本題に戻ろう。関数 ( メソッド ) の返り値は、かくあるべき、というよ うな議論はあまり類を見ないし、それゆえ、指針もないに等しい。だから、今から 手探りで探って行かなくてはならない。しかし、どうあるべきかは や開発環 、ロロロ 253