基礎プログラミング 第13回(2007年5月28日) 「関数」の補足説明 Report-Fの解説 1 ループを使わない場合,似た処理を繰 り返し書く必要があった static void Main(string[] args){ int a,b,c; do { Console.WriteLine(“正の数を入力して”); a = int.Parse(Console.ReadLine()); } while (a <= 0); do { Console.WriteLine(“正の数を入力して”); b = int.Parse(Console.ReadLine()); } while (b <= 0); do } { Console.WriteLine(“正の数を入力して”); c = int.Parse(Console.ReadLine()); } while (c <= 0); 2 ループを使うと,似た処理をまとめるこ とができた static void Main(string[] args){ int[] a = new int[3]; for(int i=0; i<3; i++){ do { Console.WriteLine(“正の数を入力して”); a[i] = int.Parse(Console.ReadLine()); } while (a[i] <= 0); } } 3 関数を使っても,似た処理をまとめるこ とができる static int InputPositiveInt(){ int x = 0; do { Console.WriteLine(“正の数を入力して”); x = int.Parse(Console.ReadLine()); } while (x<=0); return x; } static void Main(string[] args){ int[] a = new int[3]; for(int i=0;i<3;i++) a[i] = InputPositiveInt(); } 4 関数の「引数」と「返値」 Mainの中の「似た処理」を,まとめて関数に追い出 したい でも,関数にはMainの中にあるデータを処理しても らいたい また,関数に処理してもらったデータをMainの中で 受け取り,計算を続けたい 関数にデータを渡すときには「引数」で渡す 関数内で処理したデータをMain(呼び出し元)で 受け取るときには「返値」を受け取る 関数は,返値を返すときにreturn [変数]; を用いる 5 数値(値型)の場合,【値渡し】 1: static void Main(string[] args){ 2: int a = Double(2); 3: int b = Double(a); 4: Console.WriteLine(“a={0}, b={1}”, a, b); 5: } 6: static int Double(int x){ 7: x = x * 2; 8: return x; 9: } 6 2: 3: a = Double(2); int b Double(a); int a a = 4; Double(2); int b b = 8; Double(a); 変数の世界(Main) int x 2 8 4 変数の世界(Double) static int Double(int x = 2); a); x = x * 2; return x; 7 配列(参照型)の場合, 【参照渡し】 1: static void Main(string[] args){ 2: int[] a = new int[]{2,3,5}; 3: Double(a); 4: for(int i=0;i<3;i++) Console.WriteLine(“a[{0}]= {1}”, i, a[i]); // 確認用の出力 5: } 6: static void Double(int[] x){ 7: for(int i=0;i<x.Length; i++) x[i] = x[i] * 2; 8:} 8 3: Double(a); Double(a); int[] a 0 1 2 4 2 3 6 5 10 参照型の=は リンクを張る という意味 変数の世界(Main) int[] x 変数の世界(Double) static void Double(int[] x = a); x[1] = x[2] x[2] x[1] x[0] x[0] * 2; return; 9 引数の「値渡し/参照渡し」について 結局,=のときと同じ考え方でよいことになる 引数の型が値型(int, double, float, bool, etc...) なら,引数の変数(x)は関数呼出しの値(a)を 「コピーする」 引数の型が参照型(配列,String, etc...) なら,引数の変数(x)は関数呼出しの値(a) (データ)に「リンクを張る」 値型のデータを無理矢理「参照渡し」する方 法も用意されてはいる (ref, outキーワード) 基礎プロの範囲外なので説明省略 10 11回講義スライド17枚目の意図:Main関数が 値型のデータを関数に渡し,さらに関数内で処理 してもらった結果を受取るときはreturnが必要 static void Double(int x){ x = x * 2; } static void Main(...){ int a = 5; Double(a); //渡しっぱなし Console.WriteLine(a); // 画面には[5]と表示される } static int Double(int x){ x = x * 2; return x; } static void Main(...){ int a = 5; a = Double(a); //返値をaで受取った Console.WriteLine(a); // 画面には[10]と表示される } 11 例1: 自作の Array.Copy() 関数 (ただしint[] のみを受け付ける簡易版) class Array { static void Copy(int[] orig, int[] copy, int length) { for(int i=0; i< length; i++) { copy[i] = orig[i]; } } } // 使い方は,第9回の6枚目のスライドを参照すること 12 例2:引数に渡した配列のコピーを作成して返す関数 (ただし,配列要素の並び順を逆にして返す) static int[] CopyAndReverseAry(int[] x) { int[] revcopy = new int[x.Length]; for(int i=0;i<x.Length;i++) revcopy[i] = x[x.Length - i - 1]; return revcopy; } //この関数は,Report-Fのヒントになっています static void Main(...){ int[] a = new int[3]{2,3,5}; int[] reva = CopyAndReverseAry(a); } 13 もう一度,“=”の意味を再確認 =の左右は同じ型, (ただし,左は変数,右はデータとみなす) =の左右が「値型」(int, float, etc...)なら 3 =の右側(データ)を,=の左側の変数(箱)に 「代入」する =の左右が「参照型」(String, 配列, String Builder, その他のオブジェクト)なら =の左側にある「変数」のリンク先を,=の右側 (データ)に結びつける(またはリンクを張りなおす) 14 Report-F の復習[17CopyAry] intの配列を引数とし,その配列のコピーを 返す関数CopyArrayをつくってください static int[] CopyArray(int[] ary) ★動作確認プログラムもつくること!! 動作確認プログラムは, 9回目(5月14日)のプリントを 参考に,本当に「コピー」できているかどうか,までを 確認可能なものにすること (注)Array.Copy()関数の使用は禁止 (ヒント)関数内部で新しい配列の『容器』を作 成し,ループで1つ1つintの数値をコピーした あと,return 文で「作成した配列」を返す 15 Report-F 課題の意図 intの配列をコピーするCopyAry()関数をつくる Main()関数からCopyAry()関数を呼ぶ CopyAry()関数でコピーしたあとで,配列がきちんと 「コピー」されているかどうか調べる ここでの「コピー」とは,同じ値が入ったデータの実体を, オリジナルとは別に作ってください,という意味 「同じ数値が入っていることを確認してくれ」という意味ではない 「コピー」よりも,「バックアップ」と表現したほうがわかりやすかっ たかもしれない 言い換えると,「きちんと配列がバックアップされているか どうか」を調べればよい. そのためには,仮にバックアップデータを変更してみて,オリジナ ルが変更されないことを確かめればよい 16 Report-F 残念なプログラム (CopyAry()関数) static int[] CopyAry(int[] ary){ int[] copy = new int[ary.Length]; for(int i=0 ; i < ary.Length; i++) copy[i] = ary[i] * 2; //2倍を代入 return copy; } //CopyAry()関数の内部では,純粋に 配列要素のコピーだけを実行してほしかった 17 Report-F あとひといきのプログラム (CopyAry()関数) static int[] CopyAry(int[] ary){ //配列サイズ制限有 int[] copy = new int[3]; for(int i=0 ; i < 3 ; i++) copy[i] = ary[i]; return copy; } static int[] CopyAry(int[] ary){ //配列サイズ制限無し int[] copy = new int[ary.Length]; for(int i=0 ; i < ary.Length; i++) copy[i] = ary[i]; return copy; } 18 Report-F 多かった間違い(1) (Main()での確認プログラム) int[] orig = new int[3]{2,3,5}; int[] backup; backup = CopyAry(orig); // バックアップ実行 for(int i=0;i<3;i++) Console.WriteLine( “orig[{0}] = {1}”, i , orig[i]); for(int i=0;i<3;i++) Console.WriteLine( “backup [{0}] = {1}”, i , backup[i]*2); 19 Report-F 多かった間違い(2) (Main()での確認プログラム) int[] orig = new int[3]{2,3,5}; int[] backup; backup = CopyAry(orig); // バックアップ実行 for(int i=0;i<3;i++) Console.WriteLine( “orig[{0}] = {1}”, i , orig[i]); for(int i=0;i<3;i++) backup[i]*=2; for(int i=0;i<3;i++) Console.WriteLine( “backup [{0}] = {1}”, i , backup[i]); 20 Report-F 正解例 ( Main()での確認プログラム部分) int[] orig = new int[3]{2,3,5}; int[] backup; backup = CopyAry(orig); // バックアップ実行 for(int i=0;i<3;i++) backup[i]*=2; for(int i=0;i<3;i++) Console.WriteLine( “orig[{0}] = {1}”, i , orig[i]); for(int i=0;i<3;i++) Console.WriteLine( “backup [{0}] = {1}”, i , backup[i]); 21 練習問題13-1 (各自プロジェクトを作成すること) StringBuilderの配列を1つ受け取り,その 完全なコピー(フルバックアップ)を作成して返す 関数CopyStrBuildAry()を作成しなさい. static StringBuilder[] CopyStrBuildAry(StringBuilder[] sbary){ ... } 完全なコピー(フルバックアップ)とは,オリジナル,コピー それぞれが完全に独立したデータ実体を持っている状態 を指すものとする. Main()関数では,関数CopyStrBuildAry()の動作を 確認する処理を記述しなさい. ヒント:intの場合はcopy[i]=ary[i]でコピーできたが, StringBuilderの場合は=で実体コピーできないので new StringBuilder(sbary[i].ToString()) (新規作成)22 13-1 完全なコピーになっていない駄目な例 (配列の要素が共有されている) StringBuilder[] 型の変数 orig StringBuilder[] 型の変数 backup Main() StringBuilder[] 型の変数 copy (返値用) StrBuildAry() 23 13-1 期待する状態(完全なコピー) (配列の要素を共有していない.独立したデータ実体) StringBuilder[] 型の変数 orig StringBuilder[] 型の変数 backup Main() StringBuilder[] 型の変数 copy (返値用) StrBuildAry() 24
© Copyright 2024 ExpyDoc