第30講 数独(ナンバープレイス)問題解決ソフトVer.1の制作
(数独(ナンバープレイス)問題作成ソフトに挑戦する人は☆☆)
第5話 問題データ以外の欄に数字をランダムに入れるコードの解説その1
コード再掲
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
int i,j;
array<String^>^ x=gcnew array<String^>(15);
for(i=1;i<14;i++){
if(i%4==1)for(j=0;j<13;j++)x[j]=L"*";
if(i%4!=1)for(j=1;j<13;j++){
if(j%4==1)x[j]=L"*";
if(j%4!=1)x[j-1]=L"";
}
dataGridView1->Rows->Add(x);
}
}
private: System::Void button2_Click(System::Object^ sender, System::EventArgs^
e) {
int i,j;
for(i=0;i<15;i++)dataGridView1->Rows->Add();
array<String^,2>^ w=gcnew array<String^,2>(15,100);
for(i=0;i<14;i++){
for(j=0;j<14;j++){
w[i,j]=static_cast<String^>(dataGridView1[j,i]->Value);
}
}
for(i=0;i<14;i++){
for(j=0;j<14;j++){
dataGridView1[j,15+i]->Value=w[i,j];
}
}
char k1=0,k2,a[9][9];
for(i=0;i<13;i++){
if(i%4==0)k1++;
if(i%4!=0){
k2=0;
for(j=0;j<13;j++){
if(j%4==0)k2++;
if(j%4!=0){
if(w[i,j]!=L"")a[i-k1][j-k2]=int::Parse(w[i,j]);
if(w[i,j]==L"")a[i-k1][j-k2]=0;
}
}
}
}
k1=0;
for(i=0;i<13;i++){
if(i%4==0)k1++;
if(i%4!=0){
k2=0;
for(j=0;j<13;j++){
if(j%4==0)k2++;
if(j%4!=0){
if(a[i-k1][j-k2]==0)dataGridView1[j,15+i]->Value=rand()%9+1;
}
}
}
}
}
}
};
}
解説
では1行1行解説していきましょう。
まず、array<String^,2>^ w=gcnew array<String^,2>(15,100);
はString^型の2次元配列です。
配列について忘れてしまった方は、
第7講 配列と2次元ループの学習☆☆
第1話 1次元配列
第2話 2次元配列と2次元ループ
第3話 自然配列の解答と解説
第4話 前話解答と問題
第5話 積の解答例とデータ保存問題
第6話 データ保存問題解答例と列・行合計を求める問題
第7話 列・行合計を求める問題の解答と問題
第8話 7話解答
も参照していただきたいと思いますが、
ここでも解説しておきます。
一般に配列の宣言は、
array<データ型,次元数>^ 配列名=gcnew array<データ型,次元数>(第1次元の要素数,第2次元の要素数,・・・);
で行います。
C言語時代からあるCLI配列なら、
int a[5][6];
と簡単に記述できるのですが、
VC++でも使える配列であるネイティブ配列だと、
array<int,2>^ a=gcnew array<int,2>(5,6);
と記述しなければなりません。
gcnewは新しく作るという意味です。
つまり、int型の2次元配列の配列名をaとし(左辺)、
int型の2次元の配列を1次元目の要素数を5、2次元目の要素数を6として新たに作る(右辺)、
という意味になります。
array<int,2>^ a=gcnew array<int,2>(5,6);
という記述の仕方は慣れるまでは気持ち悪いのですが、文法ですから従うしかありません。
専門用語は、説明されただけではなかなかすっと頭に入ってきませんね。
ですが、インストールなど自分で使っているうちに、何の違和感もなくなります。
VC++の文法も同じです。
使い慣れるしかありません。
array<String^,2>^ w=gcnew array<String^,2>(15,100);
もString^型の2次元配列の配列名をw(これは何でいいわけですよ。xでもaでも。自分の好きなものを名前にします。)とし(右辺)、
String^型の2次元配列を第1次元の要素数15,第2次元の要素数を100として新たに作るという意味です。
2次元目の要素数は、30程度でもよかったわけですが、念のために多くとってあります。
30程度でよい理由は、問題入力で14行、解答出力で14行、両者の間に1行使うからです。
String^型の配列にした理由は、私の研究した範囲ではDataGridViewにキーボードから入力したデータを取り込むには、
String^型のデータ型にするしかないからです。
VBの方は、柔軟で
a=DataGridView(j,15+i)->Value
のように直接取り込むことが出来るのですが、
VC++の場合は、コードから入力されたデータとキーボードから入力されたデータの型が異なっているようで、
キーボードから入力されたデータを読む込むには、static_cast<String^>でキャスト(データ型の強制的変換)するしかないのです。
ですから、w[i,j]=static_cast<String^>(dataGridView1[j,i]->Value);となっているのです。
VBの場合は、キーボードから入力されたものであろうと、コード側から入れられたものであろうと、
特に区別することなく読み取れるのに対して、
VC++の場合は、キーボードから入力されたデータはstatic_cast<String^>で文字型にキャストし、
コード側から入れられたint型データは、読み込む際に(Int32)でキャストしなければならないようです。
つまり、同じように見えるデータ(例えば、3)であってもコード側から入れられたものとキーボードから入力されたものを区別しているようなのです。
dataGridViewに載っているデータは、すべてObject^型です。ですから、コード側から入れようと、キーボードが側から入れても同じはずなのですが、
Object^型変数や配列を用意して取り込んでみると、
同じデータ『L"*"』がデータ側から入れらか、キーボードから入れたかによって区別されているのです。
ですから、キーボードからL"*"を入れて(実際には、と入力)Object^型変数wで取り込んで、
if(w==L"*"){・・・}としてもこのif文はまったく無視されてしまいます。
dataGridView上のデータL"*"はキーボード出自であるのに対して、if(w==L"*"){・・・}のL"*"はコード出自であるからです。
しかし、次のようなコードならif文は有効になります。
Object^ v=L"*";
dataGridView1[12,15]->Value=v;
Object^ w;
w=dataGridView1[12,15]->Value;
if(w==L"*"){・・・}
今回は、dataGridView1[12,15]->Valueにキーボードから入れたのではなく、コード側から入れたからです。
同じObject^型のデータのはずなのに、キーボードから入力されたときと、コード側から代入されたときでは、
異なるデータとして認識されてしまうのです。
どうしてこんな面倒な設定にしているのでしょうか。
理解できません。
VBでは、データ型の違いに関しては非常に柔軟でこんな面倒は必要ないのですが。
第4話へ 第6話へ
VC++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual
Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)