int

基礎プログラミング
第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