910 likes | 1.01k Views
2.基本的なデータ構造 2.1 C#の基本的なデータ型. よく使用されるデータ型(赤字は特に注意). データ型の確認. なお, object タイプに GetTypeCode メソッドがないことに注意。. private void button1_Click(object sender, System.EventArgs e) { string A ="aaa";int B =1;object C="bbb"; MessageBox.Show(A.GetType().ToString() +":"+A.GetTypeCode().ToString());
E N D
2.基本的なデータ構造2.1 C#の基本的なデータ型2.基本的なデータ構造2.1 C#の基本的なデータ型 よく使用されるデータ型(赤字は特に注意)
データ型の確認 なお,object タイプに GetTypeCode メソッドがないことに注意。 private void button1_Click(object sender, System.EventArgs e) { string A ="aaa";int B =1;object C="bbb"; MessageBox.Show(A.GetType().ToString() +":"+A.GetTypeCode().ToString()); MessageBox.Show(B.GetType().ToString() +":"+B.GetTypeCode().ToString()); MessageBox.Show(C.GetType().ToString()); }
2.2 配列 A[0] A[1] A[2] A[28] A[29] (1) 配列:同じデータ型の集まり 宣言方法: データ型[] 配列変数名 = new データ型[要素数]; データの代入: 配列変数名[インデックス] = データ; [例] Int[] A = new int[30]; A[0]=70; A[1]=77;
(2)初期値の設定 要素数を宣言しないで, 値をまとめて代入する方法。 [形式1] データ型[] 配列変数名; 配列変数名 = new データ型[]{データ,データ,…,データ} [例] int[] A; A = new int[]{ 1, 2, 3, 4} [形式2] データ型[] 配列変数名 = {データ,データ,…,データ} [例] string[] A = {“Zero”,”One”,”Two”,”Three”}
(3)配列の初期値設定 private void Form1_Load(object sender, System.EventArgs e) { int i; int[] a=new int[]{20,26,11,32,68}; int na=a.Length; textBox1.Text = "要素数 : " + na.ToString() + "\r\n"; for (i = 0; i < na; i++) textBox1.Text += "\r\n a[" + i.ToString() + "] = " + a[i].ToString(); }
(4)配列へのデータ設定 確認用プログラム Name: textBox1 Name: button1 Name: listBox1
プログラム(その1) //宣言分部 private int 配列数=0; private int[] 配列 =new int[11]; ・ ・ ・ private void 配列の表示() { label2.Text="a[" + 配列数.ToString() + "]"; listBox1.Items.Clear(); for(int i=0; i<配列数; i++) listBox1.Items.Add("a[" + i.ToString()+ "]=" +配列[i]); }
プログラム(その2) private void button1_Click(object sender, System.EventArgs e) { try{ if(配列数>10)MessageBox.Show("配列数が10を超えました"); else{ 配列[配列数]= int.Parse(textBox1.Text); 配列数 ++; 配列の表示(); } } catch( Exception myError) { MessageBox.Show(myError.Message); } } private void Form1_Load(object sender, System.EventArgs e) { textBox1.Text=""; listBox1.Items.Clear(); }
(5)配列要素の最大値(その1) private int 最大値1(int[] a) { int i; int MAX=a[0]; if (MAX<a[1]) MAX = a[1]; if (MAX<a[2]) MAX = a[2]; if (MAX<a[3]) MAX = a[3]; if (MAX<a[4]) MAX = a[4]; if (MAX<a[5]) MAX = a[5]; return MAX; } private void button1_Click(object sender, System.EventArgs e) { int[] a = new int[]{50,20,33,55,44,25}; int b = 最大値1(a); MessageBox.Show(b.ToString()); }
(5)配列要素の最大値(その2) private int 最大値2(int[] a) { int i; int MAX=a[0]; for(i=1; i<a.Length; i++) { if (MAX<a[i]) MAX = a[i]; } return MAX; } private void button2_Click(object sender, System.EventArgs e) { int[] a = new int[]{50,20,33,55,44,25}; int b = 最大値2(a); MessageBox.Show(b.ToString()); }
(6)配列要素の逆転 方法 17 25 18 36 86 32 47 51 交換 交換 交換 交換
(6)配列要素の逆転 値の交換方法 交換後 交換前 ① X 17 17 51 51 Y 51 51 51 17 ② Temp 17 17 17 ③
(6)配列要素の逆転 確認用プログラム Name: button1 Name: button2 Name: listBox1 Name: listBox2
プログラム(その1) private int [] myArray = new int[10]; private void 配列の表示(ListBox A) { A.Items.Clear(); for(int i=0; i< myArray.Length; i++) A.Items.Add("a[" + i.ToString()+ "]=" +myArray[i]); } private void button1_Click(object sender, System.EventArgs e) { Random myRandom = new Random(); for (int i=0; i<10;i++) myArray[i]=myRandom.Next(0,100); 配列の表示(listBox1); }
プログラム(その2) private void swap( ref int X , ref int Y) { int D = X; X = Y; Y = D; } private void 配列要素の逆転(ref int[] a) { int n=a.Length; for (int i = 0; i < n / 2; i++) swap( ref a[i], ref a[n-i-1]); } private void button2_Click(object sender, System.EventArgs e) { 配列要素の逆転(ref myArray); 配列の表示(listBox2); }
(7)素数 2 力まかせ法 赤:その数で除算したが割り切れなかった。 緑:その数で除算を行ったら割り切れた。 黒:その数での除算は不要なので行わない。 3 2 2 4 3 2 4 3 5 素数 6 2 4 3 5 7 2 4 3 5 6 8 2 4 3 5 6 7 9 2 4 3 5 6 7 8 2 4 10 3 5 6 7 8 9 11 2 4 3 5 6 7 8 9 10 2 4 12 3 5 6 7 8 9 10 11 13 2 4 3 5 6 7 8 9 10 11 12
プログラム(その1) private long 素数計算1() //力まかせの方法 { int i,n; long 計算数 =0; 素数の数=0; for(n = 2; n <=1000; n++) { for (i = 2; i<n; i++) { 計算数++; // 計算回数のカウント if ((n % i) == 0) break; // 余りが0であれば割り切れた } if (n == i)素数[素数の数++] = n; // 最後まで割り切れなければ } // 素数とみなす return 計算数; } private void 表示(Label label, ListBox list,long X) { int i; label.Text="割り算回数=" + X.ToString("#,##0"); list.Items.Clear(); for(i=0; i<素数の数; i++) list.Items.Add(素数[i].ToString("#,##0")); } private void button1_Click(object sender, System.EventArgs e) { 表示(label1, listBox1,素数計算1()); }
Nより小さい素数で 割り切れるかどうかを 判定すればよい 素数の改良1 2と3を格納 2や3で割り切れなければ, 4(=2×2)や6(=2×3)でも 割り切れない。 2 3 2,3で割り切れない5を格納 2 3 5 2,3,5で割り切れない7を格納 2 3 5 7 2,3,5,7で割り切れない11を格納 2 3 5 7 11 2,3,5,7,11で割り切れない13を格納 2 3 5 7 11 13
プログラム(改良版1) private long 素数計算2() //改良1 { int i,n; long 計算数 =0; //計算回数 素数の数=0; 素数[素数の数++] = 2; 素数[素数の数++] = 3; for(n = 5; n <=1000; n += 2) { bool flag =true; for (i = 1; i<素数の数; i++){ 計算数++; // 計算回数のカウント if ((n % 素数[i]) == 0){/ / 余りが0であれば割り切れた flag=false; break; } // 余りが0であれば割り切れた } if (flag)素数[素数の数++] = n; // 最後まで割り切れなければ } // 素数とみなす return 計算数; } private void button2_Click(object sender, System.EventArgs e) { 表示(label2, listBox1,素数計算2()); }
100の約数 素数の改良2 割り切れる数の すべてが, この範囲内に 含まれる。 Nの平方根以下の素数で 割り切れることがなければ, 素数として判別してよい 2×50 4×25 5×20 10×10 20× 5 25× 4 50× 2 対称 対称 対称
プログラム(改良版2) private long 素数計算3() //改良2 { int i,n;long 計算数 =0; //計算回数 素数の数=0; 素数[素数の数++] = 2; 素数[素数の数++] = 3; for(n = 5; n <=1000; n += 2) { bool flag = true; for (i = 1; 素数[i]*素数[i] <=n; i++) { 計算数++; // 計算回数のカウント if ((n % 素数[i]) == 0) { flag=false; break; } // 余りが0であれば割り切れた } if (flag) 素数[素数の数++] = n; // 最後まで割り切れなければ } // 素数とみなす return 計算数; } private void button3_Click(object sender, System.EventArgs e) { 表示(label3, listBox1,素数計算3()); }
素数の改良3(エラトステネスのふるい)sieve of Eratosthenes (1)2以上N以下の整数をすべて書き並べておく。 (2)2の倍数をすべて消す。 (3)残った最小の数(最初は3)の倍数をすべて消す。 (4)3を繰り返す。 素数 (色つきが消された数) 2 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 3 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 5 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 7 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
プログラム(エラトステネスのふるい) private long 素数計算4() //エラトステネスのふるい(sieve of Eratosthenes) { // 割り算を一切行わない方法 int i,j; for(i=2; i<=1000;i++) ふるい[i]=i; for(i=2; i<=1000;i++) if(ふるい[i] != 0)for(j=2*i;j<=1000; j+=i) ふるい[j]=0; 素数の数=0; for(i=2; i<=1000;i++)if(ふるい[i] !=0) 素数[素数の数++]=ふるい[i]; return 0; } private void button4_Click(object sender, System.EventArgs e) { 表示(label4, listBox1,素数計算4()); }
(8)文字列と文字配列 // ツェラー(Zeller)の公式 private int WeekDay(int Y, int M, int D) { int YY=Y; int MM=M; if (M<3){YY=Y-1; MM=M+12;} return (YY +(YY/4)-(YY/100)+(YY/400)+(13*MM + 8)/5+D) % 7; } private void button1_Click(object sender, System.EventArgs e) { int Y=int.Parse(textBox1.Text); int M=int.Parse(textBox2.Text); int D=int.Parse(textBox3.Text); int R=WeekDay(Y,M,D); string X="日月火水木金土"; label4.Text = X[R]+"曜日"; } 文字列は, 文字の配列として表現される。
2.3 多次元配列 C言語では,配列を要素とする配列を多次元配列としているが, C#では,他の多くの言語と同様,自然な表現となっている。 int[,] A=newint[4,30] A[3,2] = 87; [0, 0] [0, 1] [0, 2] [0, 29] 0 [1, 0] [1, 1] [1, 2] [1, 29] 1 [2, 29] [2, 0] [2, 1] [2, 2] 2 [3, 29] [3, 0] [3, 1] [3, 2] 3 0 1 2 29 [C言語での表現] int A[4][30]; A[3][2] = 87;
2次元配列の代表例数値地図 [数値地図]各マス目に標高値が入る。 国土地理院の数値地図は,縦200×横200の約50mメッシュで,各マス目に標高値が入る。
数値地図の表示 国土地理院の数値地図(MEM)ファイルを読み込んで画面に表示する。
138° 139° 140° 136° 137° 国土地理院の数値地図の形式メッシュコード(第1次地域区画) 南端緯度の1.5倍 54/1.5=36(北度36度を示す) 西端経度の下2桁(東経138度を示す) 38 54 38度00分 37度20分 36度40分 約80Km 36度00分 35度20分 約80Km
7 6 5 4 3 2 1 0 5 6 2 4 7 3 1 0 国土地理院の数値地図の形式メッシュコード(第2次地域区画) 第1次地域区画を縦横8等分 横方向=1度/8=60分/8=7. 5分 (北緯35度で11,412m、40度で10,674m) 北になるほど狭くなる 縦方向=40分/8=5分 (北緯35度で9,246m、40度で9,252m) 北になるほど広くなるが、それほど変わらない 3 2 西からの番号(先頭を0とみなす) 南からの番号(先頭を0とみなす) 数値地図のファイル名 第1次地域区画-第2次地域区画 (例)5438-23
199 198 197 ・ ・ ・ ・ 10 9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9 10 ・・・・・・・・・・・・・・・ 197 198 199 国土地理院の数値地図の形式メッシュコード ①第2次地域区画を200×200のメッシュに分割 ②各メッシュに標高値を割り当てる
数値地図における距離 約789Km(10度) 北緯45° 44° 42° 40° 約1,664Km (15度) 38° 36° 34° 32° 30° 約965Km(10度)
地図における緯度・経度に対する弧の長さ 緯 経度1度 経度1秒 緯度1秒 度 に対する に対する に対する 弧の長さ 弧の長さ 弧の長さ (°) (Km) (m) (m) 0 111.319 30.92 30.72 5 110.899 30.81 30.72 10 109.639 30.46 30.72 15 107.550 29.88 30.74 20 104.647 29.07 30.75 25 100.950 28.04 30.77 30 96.486 26.80 30.79 35 91.288 25.36 30.82 40 85.394 23.72 30.84 45 78.847 21.90 30.87 50 71.696 19.92 30.90 55 63.994 17.78 30.92 60 55.800 15.50 30.95 65 47.176 13.10 30.97 70 38.187 10.61 30.99 75 28.902 8.03 31.01 80 19.393 5.39 31.02 85 9.735 2.70 31.02 90 0.000 0.00 31.03 30×× 沖ノ鳥島付近 38×× 宮古島付近 45×× 屋久島付近 53×× 京都・名古屋・静岡付近 60×× 秋田・盛岡北部付近 68×× 北海道北端付近 基準点 東京都港区麻布台2丁目 東経 139°44′40.5020″ 北緯 35°39′17.5148″
データ宣言と数値地図描画部 private double [,] pbJR=new double[201,201]; private string pbA; private Bitmap image; protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); e.Graphics.DrawImage(image,0,0); } private void 描画() { double MX=標高最大(); double MN=標高最小(); double DX=(MX-MN)/255; if(DX<0.000001) DX=255; Graphics g=Graphics.FromImage(image); for(int i=1; i<=200; i++) for(int j=1; j<=200; j++) { int C=(int)((pbJR[201-j,i]-MN)/DX); Brush brush = new SolidBrush(Color.FromArgb(C,C,C)); g.FillRectangle(brush,j-1,i-1,1,1); } }
表示用の共通ルーチン private double 標高最大() { double MX=pbJR[1,1]; for(int i=1;i<=200;i++) for(int j=1;j<=200;j++) if(MX<pbJR[i,j]) MX=pbJR[i,j]; return MX; } private double 標高最小() { double MN=pbJR[1,1]; for(int i=1;i<=200;i++) for(int j=1;j<=200;j++) if(MN>pbJR[i,j]) MN=pbJR[i,j]; return MN; }
国土地理院の数値地図データの読込み(形式については,提供されているCD内のドキュメントを参照)国土地理院の数値地図データの読込み(形式については,提供されているCD内のドキュメントを参照) private void openFileDialog1_FileOk(object sender, System.ComponentModel.CancelEventArgs e) { StreamReader DTS; int ii, pbII; string FName=openFileDialog1.FileName; if(FName == "") return; DTS=new StreamReader(FName,System.Text.Encoding.Default); pbA=DTS.ReadLine(); pbII=0;string DT=DTS.ReadLine(); while(DT !=null){ ii = 5; pbII=pbII+1; for(int j=1;j<=200;j++) { ii = ii + 5; pbJR[pbII, j] = int.Parse(midStr(DT, ii, 5)); } DT=DTS.ReadLine(); } DTS.Close(); 描画(); this.Invalidate(); }
文字列共通ルーチンボタンのClickイベントハンドラFormのLoadイベントハンドラ文字列共通ルーチンボタンのClickイベントハンドラFormのLoadイベントハンドラ private string midStr(string DT, int ist,int N) { string S="";int k=ist-1; for(int i=1 ;i<=N;i++){S = S + DT[k]; k=k+1;} return S; } private void button1_Click(object sender, System.EventArgs e) { openFileDialog1.ShowDialog(); } private void Form1_Load(object sender, System.EventArgs e) { image =new Bitmap(200,200); }
経過日数の計算 多次元配列は,何らかの条件で値が異なるようなテーブルデータとして 用いられることも多い。ここで示す例は,うるう年と通常年のときの 月の日数の違いを表で表した例です。 [画面の定義] Name: textBox1 Text : 空文字列 Name: textBox2 Text : 空文字列 Name: textBox3 Text : 空文字列 Name: button1 Text : 日数を求める Name: label4 Text : 空文字列
プログラム private int[,] 月日数= new int[,] { {31,28,31,30,31,30,31,31,30,31,30,31}, {31,29,31,30,31,30,31,31,30,31,30,31}, }; private int うるう年判定(int Y) { return (Y % 4 ==0 && Y % 100 !=0 || Y % 400 ==0)? 1:0; } private int 経過日数(int Y, int M, int D) { int i; int 日数=D; int K= うるう年判定(Y); for (i=1; i<M; i++) 日数 += 月日数[K,i-1]; return 日数; } private void button1_Click(object sender, System.EventArgs e) { int Y=int.Parse(textBox1.Text); int M=int.Parse(textBox2.Text); int D=int.Parse(textBox3.Text); label4.Text=経過日数(Y,M,D).ToString(); }
2.4 構造体(1)考え方(その1) あるグループの身長と体重のデータが得られたものとして, その統計分析を行うことを考えます。 それぞれは,次のように配列で表現することもできます。 氏 名 体重 身長 佐 多 憲 二 65 171 村 瀬 真 57 169 大 田 和 夫 60 168 北 田 洋 一 73 172 志 村 康 司 62 170 別 府 拓 哉 59 167 相 馬 修 一 68 176 木 田 裕 也 61 167 これらのデータ間の関係は,プログラム内で直接的に表現されない。
(1)考え方(その2) プログラム内で, 次のようなカード形式に似た形で表現できれば, 直接的な関係が分かります。 氏名:木 田 裕 也 体重:65kg 身長:171c 氏名:相 馬 修 一 体重:65kg 身長:171c 氏名:別 府 拓 哉 体重:65kg 身長:171c 氏名:志 村 康 司 体重:65kg 身長:171c 氏名:北 田 洋 一 体重:65kg 身長:171c 氏名:大 田 和 夫 体重:65kg 身長:171c 氏名:村 瀬 真 体重:65kg 身長:171c 氏名:佐 多 憲 二 体重:65kg 身長:171c
(1)考え方(その3)構造体の宣言 異なるデータの型でもひとまとまりのデータとして扱う方法, これが構造体です。まず,以下のように構造体の宣言を行います。 public struct 体格データ { public string 氏名; public double 体重,身長; } (a) 体格データ 「体格データ」という型名。構造体タグ(structure tag)と呼ぶ。 (b) 氏名,体重,身長 構造体の要素。構造体メンバ(structure member)と呼ぶ。 なお,たまたま構造体メンバの型がすべて同じでもかまいません。
構造体データの使用 データを使うためには,まず,通常の変数の型宣言と同じように 以下のように宣言して使います。 体格データ X; 参照したり,設定するときは,次のようにドット(.)をつけて 構造体メンバ名を指定します。 X.氏名=textBox1.Text; X.体重=double.Parse(textBox2.Text); X.身長=double.Parse(textBox3.Text);
プログラム例 [画面定義] Name: textBox1 Text : 空文字列 Name: textBox2 Text : 空文字列 Name: button1 Text : 計算 Name: textBox3 Text : 空文字列
プログラム例 public struct 体格データ { public string 氏名; public double 体重,身長; } public class Form1 : System.Windows.Forms.Form { ・ ・ ・ private void button1_Click(object sender, System.EventArgs e) { 体格データ X;string S; X.氏名=textBox1.Text; X.体重=double.Parse(textBox2.Text); X.身長=double.Parse(textBox3.Text); double 標準体重=X.身長*X.身長*22/10000; double 肥満度=(X.体重-標準体重)/標準体重; if(肥満度>=0.1)S=“太りすぎ”; else if(肥満度<=-0.1) S=“やせすぎ”;else S="標準"; MessageBox.Show(X.氏名+“さんは,” + S + “です。肥満度 = ” +肥満度.ToString("#0.00")); } }
(2)構造体の配列 構造体も配列として扱うことができます。 たとえば,以下のように宣言します。 public struct 体格データ { public string 氏名; public double 体重, 身長; } public class Form1 : System.Windows.Forms.Form { ・ ・ ・ private 体格データ[] Z=new 体格データ[8];
配列として宣言された構造体の使用 代入や参照では, 構造体の配列名[インデックス].構造体メンバ名 として指定します。 [例] Z[2].氏名=“大 田 和 夫”; Z[2].体重=60; Z[2].身長=168;
プログラム例 [画面定義] Name: listBox1 Name: button1, Text : 計算 Name:label1
プログラム例(体格データの構造体宣言は,(1)と同じプログラム例(体格データの構造体宣言は,(1)と同じ private 体格データ[] Z=new 体格データ[8]; private void button1_Click(object sender, System.EventArgs e) { double T1=0;double T2=0; for(int i=0;i<Z.Length;i++) { T1 += Z[i].体重;T2 += Z[i].身長; } T1=T1/Z.Length;T2=T2/Z.Length; label1.Text=" 平均体重 = "+T1.ToString() +" 平均身長 = "+T2.ToString(); }
プログラム例(初期設定) private void Form1_Load(object sender, System.EventArgs e) { Z[0].氏名="佐 多 憲 二";Z[0].体重=65;Z[0].身長=171; Z[1].氏名="村 瀬 真";Z[1].体重=57;Z[1].身長=169; Z[2].氏名="大 田 和 夫";Z[2].体重=60;Z[2].身長=168; Z[3].氏名="北 田 洋 一";Z[3].体重=73;Z[3].身長=172; Z[4].氏名="志 村 康 司";Z[4].体重=62;Z[4].身長=170; Z[5].氏名="別 府 拓 哉";Z[5].体重=59;Z[5].身長=167; Z[6].氏名="相 馬 修 一";Z[6].体重=68;Z[6].身長=176; Z[7].氏名="木 田 裕 也";Z[7].体重=61;Z[7].身長=167; for(int i=0;i<Z.Length;i++) listBox1.Items.Add(Z[i].氏名+": 体重 = " +Z[i].体重+"kg 身長 = "+Z[i].身長); label1.Text=""; } }
(3)構造体を使った演算子の定義 ここでは,構造体を使った演算子の定義の例として 複素数を取り上げます。 複素数は,実部と虚部に分けることができますので, 構造体で表現すると次のようになります。 public struct Complex { public double R; // 実部 public double I; // 虚部 }