第4講 if文の学習

第12話 並び替えソース例解説
ソース例
#pragma endregion
   private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
           int i,j,max,min,w;
                 ・
                 ・
                 ・
             //合計・平均の表示
             dataGridView1[6,i]->Value=w;
             dataGridView1[7,i]->Value=((double)w)/5;
                ・
                ・
                ・
           //データ並び替え
           int bn;
           
Object^ w1; //Object^型変数の用意
 
           for(i=40;i>=2;i--){
             min=500;
             for(j=1;j<=i;j++){
               if((int)(dataGridView1[6,j]->Value)<=min){
                 min=(int)(dataGridView1[6,j]->Value);
                 bn=j;
               }
             }
             for(j=0;j<=12;j++){
               w1=dataGridView1[j,bn]->Value;  //キャストが必要ない
               dataGridView1[j,bn]->Value=dataGridView1[j,i]->Value;  //ここもキャストは必要ない
               dataGridView1[j,i]->Value=w1; //ここもキャストは必要ない
             }
           }

                
}

まず変更部分である赤から説明しましょう。
最初wはdouble型で用意しました。
理由は、後に平均を計算するからです。
C++では割り算に対応するためには、double型を用意しなければなりません。
しかし、合計値の計算においては整数の足し算ですから、
double型にする必要がありません。
今回この合計値を使って、並び替えを行います。
なかなか、原因が解明できなかったのですが、
wをdouble型にしておくと、
                 min=(int)(dataGridView1[6,j]->Value);
のときに無効なキャストであると出てきてしまいます。
元々のデータがdouble型であり、それを
             dataGridView1[6,i]->Value=w;
のおいてObject^型してdataGridViewに収めていたのを
Object^型をint型に変更しようとしていたわけです。
結局Object^→(double型)→int型と2つ飛びのキャストしようとしていたので、
エラーしたものと思われます。
もっともwをdouble型にしておいて
                 min=(double)(dataGridView1[6,j]->Value);
としてもキャストは有効でにないと出てきてしまいますから、
Object^型からdouble型へのキャストはもともとフォローしていないと解釈すべきかもしれません。
実際、wをint型に変更するとエラーしません。
それで、int型にしたわけですが、
dataGridView1[7,i]->Value=w/5;
だと右辺が整数に丸められてしまいます。
そこで、強制的にwをdouble型にキャスト(変換)したわけです。
ただし、((double)w)は変数w自体をdouble型に変更してしまうわけでは合いません。
変数の中身だけキャスト(変換)するとお考えください。
箱はint型のままで、箱の中のデータだけをdouble型に変更するわけです。
並び替え

さて、いよいよメインの2次元ループを解説しましょう。
           int bn;
           Object^ w1; //Object^型変数の用意
 
           for(i=40;i>=2;i--){
             min=500;
             for(j=1;j<=i;j++){
               if((int)(dataGridView1[6,j]->Value)<=min){
                 min=(int)(dataGridView1[6,j]->Value);
                 bn=j;
               }
             }
             for(j=0;j<=12;j++){
               w1=dataGridView1[j,bn]->Value;  //キャストが必要ない
               dataGridView1[j,bn]->Value=dataGridView1[j,i]->Value;  //ここもキャストは必要ない
               dataGridView1[j,i]->Value=w1; //ここもキャストは必要ない
             }
           }
ループ文を読む上で大切なことは、
各変数の役割を把握することです。
ここでは、i,j,min,bnが登場します。
jは2回登場しますが、1回目と2回目では役割が全然違います。1人2役です。

iは、探索範囲行です。
データは2行目から入っていますが、DataGridViewでは1行目を0行目と数えますので、
出席番号とiが一致しております。
始めは40から始まりますので、40行が、つまり出席番号1番から40番までが、探索範囲です。
次にi--によって一つ減りますから、39行が探索範囲です。
1行探索範囲が減ったのは、1回目の探索で40人中の最低点者(同点者が複数いた場合は、番号最下位者)を明らかにしているので、
その最低点者の順位は一応40位に確定しますので、
探索対象から外すのです。
2回目も最低点者を確定して、探索から外し次は38人が探索範囲になります。
以下同様にして、それぞれの人数の中の最低点者を確定し除外を繰り返すことによって、
すべての順位を確定しようとしているわけです。


             min=500;
は、5教科の合計なので最低点が500点ということも理論的にはあり得ますので、今回はmin=500; としています。
もし、全員が全教科満点なら最低点は500点ですよね。
もちろん、実際にはそんなことはあり得ません。
もし、コンピュータで宇宙時間(宇宙の始まりから終わりまでの時間)試行実験を繰り返させたとしても1回も表れないでしょう。
ですが、minはどんなに大きくとっても問題ありません。すぐに、
               if((int)(dataGridView1[6,j]->Value)<=min){
                 min=(int)(dataGridView1[6,j]->Value);
                 bn=j;
               }
でデータが修正されるからです。むしろ、間違い得ないように大きな値にしておいた方がよいのです。

jの1回目
             for(j=1;j<=i;j++){
               if((int)(dataGridView1[6,j]->Value)<=min){
                 min=(int)(dataGridView1[6,j]->Value);
                 bn=j;
               }
             }
の役回りは何でしょうか。このjは探索行を表しています。
つまり、1行目からi行目までが探索範囲です。
iは前の説明の通り、40→39→38→・・・と人ずつ小さくなっていきます。
したがって、iの1回目ではjは1から40まで動き、
iの2回目では1から39まで動き、
iの3回目では1から38まで動き、
       ・
       ・
       ・
となります。
1回1回探索範囲は一つずつ小さくなっていきますが、その探索行すべてを動くのが1回目のjの役割です。
では、
             for(j=1;j<=i;j++){
               if((int)(dataGridView1[6,j]->Value)<=min){
                 min=(int)(dataGridView1[6,j]->Value);
                 bn=j;
               }
             }
の任務は何でしょうか。探索行中の最低点とその人の出席番号を見いだすことです。
dataGridView1[6,j]->Value)<=>minと=が入っていますので、同点者がいた場合出席番号が大きい方が優先されます。
例えば、出席番号26番の人と35番の人が同じ点数(仮に15点とします。)で最低点者であるとします。
するとj=26のときに、if文が実行され、min=15、bn=26となります。
また、j=35のときif文の条件式の中身は15<=15となり、これは正しいのでここでもif文は実行され、
min=15,bn=35となりますから、同点の場合出席番号が遅い方が対象者に選ばれます。
もちろん、出席番号が若い方を対象者に選びたいなら dataGridView1[6,j]->Value)<=minを
dataGridView1[6,j]->Value)<minに変更すればよいのです。これならば、
j=35のときは、if文の条件式の中身は15>15となりこれは正しくないのでif文は実行されませんので、
jn=26は更新されず、出席番号26番が最低点者の代表に選ばれることになります。

jの2回目
             for(j=0;j<=12;j++){
               w1=dataGridView1[j,bn]->Value;  //キャストが必要ない
               dataGridView1[j,bn]->Value=dataGridView1[j,i]->Value;  //ここもキャストは必要ない
               dataGridView1[j,i]->Value=w1; //ここもキャストは必要ない
             }
においてはjはどんな役を演じているのでしょうか。2回目の役は、列数です。
ソート
A列からM列は列数でいうと、0列から12列です。
したがって、j=0;j<=12;j++によって対象にされる列は、A列からM列です。
ではそれを対象にして、
             for(j=0;j<=12;j++){
               w1=dataGridView1[j,bn]->Value;  //キャストが必要ない
               dataGridView1[j,bn]->Value=dataGridView1[j,i]->Value;  //ここもキャストは必要ない
               dataGridView1[j,i]->Value=w1; //ここもキャストは必要ない
             }
では何をしているのでしょうか。w1を何のために用意したか思い出せば答えは明らかです。
もし、iの1回目なら35番(先に想定した例の場合)と40番の人の行丸ごとの交換をするのが、この部分の任務です。
トレースしてみましょう。
j=0のとき、
w1=dataGridView1[j,bn]->Value; でw1=dataGridView1[0,35]->Value;となり、w1には35が入ります。
セル
    ・
    ・
Celliの1回目においては、w1=dataGridView1[0,35]->Value;
35と出席番号35は一致していましたね。35はもちろん図の赤い囲いの35です。
すなわち出席番号です。
dataGridView1[j,bn]->Value=dataGridView1[j,i]->Value; でdataGridView1[0,35]->Value=dataGridView1[0,40]->Value;
となり、40行目(これは40番でなくなっていると考える方が妥当です。j=35になる前にすでに交換されている可能性が高いからです。)
の出席番号が35行目に入ります。そして、
dataGridView1[j,i]->Value=w1; によってdataGridView1[0,40]->Value=w1; となり、
40行目の出席番号が35となるわけです。
結局、3行によって0列目(すなわちA列)の35行目と40行目が交換されていいます。
以下、j=1なら1列目(すなわちB列)の35行目と40行目が、
j=2なら2列目(すなわちC列)の35行目と40行目が、
j=1なら3列目(すなわちD列)の35行目と40行目が、
        ・
        ・
        ・
と交換されていって行ごとの交換が完了するわけです。
尚、今回はOject^型の変数w1を用意したので、
w1=(int)(dataGridView1[j,bn]->Value;) 
のようなキャストをすると逆にエラーします。
キャストをせずに直接代入
w1=dataGridView1[j,bn]->Value; 
することになります。

では課題を出してこの話を終了しましょう。
成績一覧表詳細
順位欄にデータを入れてください。
単に1,2,3,・・・,40と入れたのでは、同点者がいた場合正しい順位ではありません。
if文を使って、正しい順位になるように工夫してください。
そして、順位付けが終わったならもう一度出席番号順に並び替えして、この講の課題(成績一覧表の作成)は成就したことになります。

したがいまして、この講はいよいよ第13話をもって終了となります。

今回は、初心者には難しい題材をテーマとして選んでしまったため13話構成になってしまいましたが、
基本は平均8話を目標にしています。
すなわち、講座全体で80話構成を目標にしています。

尚、出席番号順に並び替えるときには
dataGridView1[0,i]->Value =i+L" ";  //+L" "は文字をセンタリングするために入れていました。

dataGridView1[0,i]->Value =i;
と変更しておいてください。
前者だとデータはいったん文字型にキャストされてしまうので、
(int)(dataGridView1[0,j]->Value)としてデータを取り込もうとするときにエラーします。


第11話へ 第13話へ

025


vc++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座へ

数学研究室に戻る