2007年5月27日日曜日

プログラムの考え方 その2

引き続きhttp://blog.livedoor.jp/clausemitz/archives/50656193.htmlより引用

どうも、この問題、根が深いらしい。というのはプログラミングというのは
ある程度、「ひらめき」が要求されるもので、苦手な人はそもそも、その
ひらめきが生じないらしい。プログラミングは理数系という勘違いも拍車を
かけている気がする。聞くところによれば理数系の大学院まで行って
どう見ても私のような高卒DQNより、はるかに優秀な頭脳の持ち主が
簡単なプログラミングができなくて、頭をかかえている例もあるらしい。

ところで、あなたは理系ですか文系ですか、というつまらない質問があるが
私だったら「体育系」ですと答えるだろう。プログラミングしている時の
私の頭の中は動物的な勘がぐるぐる渦巻いているような感じで理路整然と
演繹的に問題を解いているのではなく、あっちこっち試行錯誤をしていて
言語として抜き出しにくい状態、いわゆる「暗黙知」が支配する感じだ。

このあたりの感触をわかっている、わかっていないが
プログラミングが得意か、不得意かの分かれ目のような気がするが、
他人の思考を覗き見できない(説明を求めても、うまく答えてくれた試しがない)
ので、今のところ、推測にすぎない。

で、FizzBuzz問題で、頭をかかえる人は、おそらく、ここでつまづいている
という「推測」はできるのだけど、他人の思考を覗き見できない
(なおかつ、できない人は、なぜできないかの説明すらできない)
ので、そのあたりをチクチク責めるよりも
私だったら、こう解いたという道筋を分解して説明しようと思う。

プログラミングの苦手な人は、この「分解」ができていません。

●1~100の数字を表示するプログラムを作る。

ダメな人の特徴として、いきなり「完成形」を作ろうとするのがある。
まず最初はできるところから、ラフスケッチから攻めるべきだ。

「3で割り切れるなら…」「5で割り切れるなら…」という仕様はまず無視。

1~100を間にカンマをつけて表示するから始めよう。ところがこの程度でも
いきなりつまづく人がいる。カンマのつけかたをどうするか。

 int aIdx;

 for(aIdx = 1; aIdx <= 100; aIdx++){
  printf(", %d",aIdx);
 }
 printf("\n");

これだと「, 1, 2…」と表示するから、最初の1だけカンマをつけないなら
1だったらカンマをつけない、1以外はカンマをつけるという「条件分岐」を
ここで盛り込む。

 int aIdx;

 for(aIdx = 1; aIdx <= 100; aIdx++){
  if(aIdx > 1){
   printf(", ");
  }
  printf("%d",aIdx);
 }
 printf("\n");

●3で割り切れるならFizzを出す

単純に考えれば、printf("%d",aIdx);を

 if(aIdx % 3 == 0){
  printf("Fizz");
 }else{
  printf("%d",aIdx);
 }

に入れ替えだが、これが「ひっかけ」だと気づくか気づかないかで運命が決まる。
「5で割り切れるなら…」という仕様が控え、さらに「3と5で割り切れるなら…」
という仕様があるから単純に条件分岐するのはまずい。ではどうするか?
「状態」を保持しておき、分岐は剰余演算の直接結果ではなく、状態で行う。
「3で割り切れるか割り切れないか」の状態を示す変数、aFizzを導入する。

 int aIdx,aFizz;

 for(aIdx = 1; aIdx <= 100; aIdx++){
  if(aIdx > 1){
   printf(", ");
  }
  aFizz = (aIdx % 3 == 0);
  if(aFizz == 0){
   printf("%d",aIdx);
  }else{
   printf("Fizz");
  }
 }
 printf("\n");

●5で割り切れるならBuzzを出す

ここまで来れば、次のステップは
「5で割り切れるか割り切れないか」の状態を示す変数、aBuzzを導入する。
に気づくから(気づかないならプログラマーはやめたほうがいい、マジで)

 int aIdx,aFizz,aBuzz;

 for(aIdx = 1; aIdx <= 100; aIdx++){
  if(aIdx > 1){
   printf(", ");
  }
  aFizz = (aIdx % 3 == 0);
  aBuzz = (aIdx % 5 == 0);

と、ここまでできれば数字を表示するのはFizzでもBuzzでもない状態だから

  if(aFizz == 0 && aBuzz == 0){
   printf("%d",aIdx);

とすりゃいいんだけど、ここで頭をかかえる人はFizz,Buzzの表示方法を
どうするかでしょうね。単純にaFizzでFizzを出す、aBuzzでBuzzを出すだと
両方が成立するケースでうまく表示できない(間のスペースをどう表示する)。
(switch(aFizz + aBuzz)で分岐すると短くなるけど、いかにもC言語だな…)
ここは変に技巧をこらさずにストレートに行きましょう。

  }else{
   if(aFizz != 0){
    printf("Fizz");
   }
   if(aBuzz != 0){
    if(aFizz != 0){
     printf(" ");
    }
    printf("Buzz");
   }
  }
 }
 printf("\n");

と、まあこんな感じで解いているのですが、ここで説明していない理由で
コーディングを決めているものもあって、それもおそらくプログラミングが
苦手な人がつまづいている要因なのかもしれませんね…。

 *

FizzBuzz問題と関連するのかどうかわからないですが面白い記事を発見しました。

プログラマーになれる人なれない人

どういう理屈なのかはわからないけど、プログラミングを出来るようになれる人とマッタク出来ない人はハッキリと分かれてしまう。

俺がコンピュータサイエンス学科に居た頃と Teaching Assistant として一年生の面倒見てた経験から言うと、プログラミングが出来ない人はホントに最初の段階から出来ない。

例えば、

  int a = 10;

というのを習うと 50 人中 4,5 人は脱落する。
変数という概念がどうしても腹の中におちていかないらしい。

にわかには信じがたいですが、変数なんて便利な概念を理解できずして
よく生きてこれたと感心します。(皮肉ではなく正直に感心してます)


ものすごく大雑把に言うと 4 割から 7 割の人はプログラマーに向いてない、ということになる。ポインタと再帰はかなりの難関だけど、 それ以前の段階でつまづいている人がかなりいるのでは、と俺は思う。ポインタと再帰までは騙し騙しでやれてるだけなんじゃないかなぁ?

ごめん。ポインターと再帰は私は簡単に理解してしまった。

ていうか、お勉強があまりできない高卒の私ですら理解できたことだから
世間一般のプログラマーは全員、簡単に理解したものとばかり思ってた。

だから指導者にはなれないんだろうな…。なるつもりはないけど。

0 件のコメント: