講義メモ 第8章 クラスとメソッドの詳細

p.181 メソッドの再帰呼び出し

・C#などのメソッドは内部で自分自身を呼び出すことが可能で、これを再帰(リカージョン)という
・再帰を用いることで、アルゴリズムを単純化できることがある
・なお、再帰を用いる場合は「場合によっては自分自身を呼び出さず、値を返す」処理が必要で、
 これがないと無限ループする
 ※「再帰の終了条件」ともいう

p.181 階乗を計算する

・階乗とはある正の整数において、その数から1までの全整数の積のことで「整数!」と表す
・例: 3! = 3×2×1 = 6、4! = 4×3×2×1 = 24
・階乗は繰返し構文で計算可能だが、n! = n × (n - 1)! であることを用いて再帰でシンプルに記述できる
・例: 3! = 3×2×1 = 6、4! = 4×(3×2×1) = 4×3!
・なお、0の階乗及び1の階乗は1と定められているので、p.182 fact01.csでは0の階乗が1であることを
 再帰の終了条件にしている

アレンジ演習:p.182 fact01.cs

・0の階乗が1であることを再帰の終了条件にしているが、1の階乗も1であることを加味して効率化しよう
・条件演算子を用いてCalcFactメソッドを簡略化しよう

作成例

//アレンジ演習:p.182 fact01.cs
using System;
class Fact {
    public long CalcFact(int n) { //n!を返す再帰を含むメソッド
        return (n == 0 || n == 1) ? 1 : n * CalcFact(n - 1); //0!=1!=1、n!=n*(n-1)!
    }
}
class fact01 {
    public static void Main() {
        Fact f = new Fact();
        for (int i = 0; i <= 20; i++) {
            Console.WriteLine("{0}! = {1}", i, f.CalcFact(i));
        }
    }
}

p.185 フィボナッチ数列を求める

・フィボナッチ数列とは先頭2要素の値を1とし、3要素目以降は前2要素の和を値とする数列
・例: 1, 1, 2, 3, 5, 8, 13, 21, 34,…
・各種のシミュレーション、統計処理に利用されている
・これは、先頭を1要素目とすると、3要素目は1要素目+2要素目なので、n要素目は
 「(n-2)要素目+(n-1)要素目」
 という再帰表現にできる
・なお、再帰の終了条件は「1要素目と2要素目は1」を用いると良い

アレンジ演習:p.185 fibonacci.cs

・条件演算子を用いてメソッドを簡略化しよう

作成例

//アレンジ演習:p.185 fibonacci.cs
using System;
class fibo {
    public long CalcFibo(int n) { //フィボナッチ数列のn番目の値を返す(先頭を1番目とする)
        return (n == 1 || n == 2) ? 1 : CalcFibo(n - 1) + CalcFibo(n - 2); //再帰を含む
    }
}
class fibonacci {
    public static void Main() {
        fibo f = new fibo();
        for (int i = 1; i <= 30; i++) {
            Console.WriteLine("f({0}) = {1}", i, f.CalcFibo(i));
        }
    }
}

p.188 refとout:前提の説明「値渡しについて」

・メソッドの仮引数には、実引数で指定された値や式の評価の値が渡される
・このことを値渡しという
・p.189 swap01.csは、Swapメソッドの中で仮引数の値を書き換えており、実引数の値が書き換わると考えたプログラム
・実引数と仮引数が同じ名前なので誤解を招きやすいが、コピーされた値が用いられるので、実引数の値は変化しない

p.190 refとout:前提の説明「参照型における値渡しについて」

・他の言語では参照型の引数を用いると、値渡しにならず、実引数に参照型変数を指定し、
 仮引数を操作すると実引数が書き換わるものもあるが、C#では値渡しになる
・よって、p.190 swap02.csは、p.189 swap01.csと同様になり、実引数の値は変化しない

p.191 refとout:前提の説明「配列を実引数にすると参照渡しになる」

・p.191 changearrray01.csの通り、配列を実引数にすると配列の位置情報(参照)が値として渡されるので、
 仮引数も同じ配列を指すことになる
・よって、配列を実引数にすると値渡しでなく、参照渡しと呼ばれる受け渡しになる
・よって、仮引数である配列の要素値を操作すると、実引数である配列の要素値が変化する
・もちろん、仮引数である配列と実引数である配列の配列名は同じである必要はない

アレンジ演習:p.191 changearrray01.cs

・「new int[3]」を省略して、配列の末尾に要素「4」を追加しよう
・すると、プログラムの他の部分をまったく変更しなくてよいことを確認しよう

作成例

//アレンジ演習:p.191 changearrray01.cs
using System;
class change {
    public void modify(int[] array) { //配列を受け取るメソッド(参照渡しになる)
        int n = array.Length; //受け取った配列の要素数を得る
        for (int i = 0; i < n; i++) { //全要素について繰返す
            array[i] *= 2; //要素値を2倍にする(参照渡しなので呼び出し側で指定した配列が書き換わる)
        }
    }
}
class changearray01 {
    public static void Main(){
        change c = new change();
        int[] myarray = {1, 2, 3, 4}; //【変更】
        Console.WriteLine("----modifyメソッド実行前----");
        int i = 0;
        foreach (int x in myarray) { //全要素について繰返す
            Console.WriteLine("myarray[{0}] = {1}", i, x); 
            i++;
        }
        c.modify(myarray); //配列をメソッドに渡して書き換えてもらう
        Console.WriteLine("----modifyメソッド実行後----");
        i = 0;
        foreach (int x in myarray) { //全要素について繰返す
            Console.WriteLine("myarray[{0}] = {1}", i, x);
            i++;
        }
    }
}

テキスト正誤 p.191 changearrray01.cs 1行目

【誤】// charngearrray01.cs
【正】// changearrray01.cs

ミニ演習:p.191 changearrray01.cs ⇒ changearrraylist.cs

・changearrray01.csの配列をArrayListオブジェクトに変更して、動作を確認しよう

作成例

//ミニ演習:p.191 changearrray01.cs ⇒ changearrraylist.cs
using System;
using System.Collections; //【追加】
class change {
    public void modify(ArrayList array) { //【変更】アレイリストを受け取るメソッド(参照渡しになる)
        int n = array.Count; //【変更】受け取ったアレイリストの要素数を得る
        for (int i = 0; i < n; i++) { //全要素について繰返す
            int w = (int)array[i] * 2; //【変更】要素値を取り出して2倍にする
            array[i] = w; //【追加】再格納する
        }
    }
}
class changearray01 {
    public static void Main(){
        change c = new change();
        ArrayList myarray = new ArrayList(); //【追加】アレイリストのオブジェクトを生成
        for (int n = 1; n <= 4; n++) { //【以下追加】
            myarray.Add(n); //アレイリストに格納
        }
        Console.WriteLine("----modifyメソッド実行前----");
        int i = 0;
        foreach (int x in myarray) { //全要素について繰返す
            Console.WriteLine("myarray[{0}] = {1}", i, x); 
            i++;
        }
        c.modify(myarray); //アレイリストをメソッドに渡して書き換えてもらう
        i = 0;
        foreach (int x in myarray) { //全要素について繰返す
            Console.WriteLine("myarray[{0}] = {1}", i, x);
            i++;
        }
    }
}

p.192 refとout:refキーワード

・ここまでの説明の通り(配列のような構造を渡す場合を除いて)引数においては値渡しが基本であり、
 メソッド側で仮引数の値を操作しても、実引数には反映しない。
・これを参照渡しに切り替えるのがrefキーワード
・実引数と仮引数の双方にrefキーワードを与えると、メソッド側で仮引数の値を操作すると実引数には反映する。
・実引数名と仮引数名が異なっても構わない。
・なお、refキーワードを付けて用いる実引数はなんらかの値が代入されている必要がある
・また、呼び出し側で式やリテラルを実引数として与えると文法エラーになる

アレンジ演習:p.193 swap03.cs

・「refキーワードを付けて用いる実引数はなんらかの値が代入されている必要がある」ことを試そう
・「呼び出し側で式やリテラルを実引数として与えると文法エラーになる」ことを試そう
・どちらも、確認したらコメントアウトすること

作成例

//アレンジ演習:p.193 swap03.cs
using System;
class myclass {
    private int temp;
    public void swap(ref int x, ref int y) { //仮引数xとyは参照渡しになる
        temp = x;
        x = y; //この時点で仮引数xが参照している実引数に代入
        y = temp; //この時点で仮引数yが参照している実引数に代入
    }
}
class swap01 {
    public static void Main() {
        myclass s = new myclass();
        int x = 10, y = 20;
        s.swap(ref x, ref y); //実引数xとyは参照渡しになる(値が書き換わる)
        Console.WriteLine("x = {0}, y = {1}", x, y); //値が交換されている
        //int z;
        //s.swap(ref x, ref z); //「未割当のローカル変数zが使用された」という文法エラーになる
        //s.swap(ref x, ref(x + y)); //「割当て可能な変数でなければならない」という文法エラーになる
        //s.swap(ref x, ref(200)); //「割当て可能な変数でなければならない」という文法エラーになる
    }
}

p.192 refとout:outキーワード

・refキーワードの上位版がoutキーワードで、refの「実引数はなんらかの値が代入されている必要がある」という制限がない
※C#のバージョン7以降では、outキーワードの仕様が強化され、outキーワードで指定する実引数は、
 事前の定義が不要になり「out 型 実引数名」とすれば良い。
 この実引数は呼出し以降にそのまま利用可能。

アレンジ演習:p.194 outkeyword01.cs

・変数cの定義を省き、実引数の指定を「out double c」とできることを確認しよう

提出:アレンジ演習:p.193 swap03.cs、または、アレンジ演習:p.194 outkeyword01.cs

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です