プログラミング/5

出典: CourseWiki

目次

アルゴリズム

今まで見てきたように,コンピュータに問題を解決させるためには,決まった手順が必要です.このような手順のことをアルゴリズム(algorithm) (日本語では算法)と言います.アルゴリズムとは,ある目的を実現するために必要な手順のことで,手順の各段階で何を行うかが明確に述べられている必要があります.つまり,誰でもその手順に従えば同じ結果が得られるようなものでなければなりません. アルゴリズムを特定のプログラミング言語で記述したものがプログラムになります.

ちなみに,ユークリッドの互除法は,英語では Euclid's Algorithm (ユークリッドのアルゴリズム)といいます.

バグについて

プログラムを書くときに間違いは付き物です.プログラムの間違いを一般にバグ(bug)と呼びます. また,バグを取り除く作業をデバッグ(debug)と呼びます.バグには主に文法上のバグとアルゴリズムのバグの2通りあります. 文法上のバグは,ソースプログラムがプログラミング言語の文法に従っていないというミスで,コンパイラが報告します. アルゴリズムのバグは,ある問題を解決するための方法そのものに間違いがあったというものです.プログラムはコンパイルできて動作するけれども,実行すると思った結果が得られません.一般に,こちらの方が深刻なバグと考えられています.

バグに興味のある人へ:

デバッグの方法

ソースプログラムの文法上のバグはコンパイラが報告するので,それをヒントに直します.複数の間違いが報告される場合がありますが,基本は「最初のバグを直す」です.

コンパイル・実行はできるものの,プログラムの挙動がおかしいという場合,ソースプログラムを良く見て中で何が起きているのかを追跡する必要があります.

追跡にはデバッガ(debugger)と呼ばれるデバッグ用のプログラムを利用する場合もありますが(Eclipseにも内蔵されています), この授業で作る程度の規模のプログラムでは特に必要ないでしょう.

ちなみに,デバッガでは一般に次のようなことが可能です.

  • ソースプログラムの指定した行で一時停止
  • 指定した条件が満たされたら一時停止
  • 変数の値の表示
  • メソッドの呼び出し階層の表示

実用的な規模のプログラムを作成する際にはデバッガは必需品と言えます.

デバッグで良く使われる手法は,「とりあえず画面に表示してみる」です.怪しいとにらんだところに,System.out.println(..) 等を追加して自分が思ったように動いているかを確認しましょう.例えば,

  • if文の中が実行されているか確信が持てないならば,if文の中に System.out.println("chk1"); などを追加してみる.
  • 繰り返しがうまく動いていないならば,繰り返しの中でループ変数の値や繰り返し条件に関係する変数を表示してみる.

このような,デバッグのためにプログラムに画面出力を追加することは簡単でよく行われますので, 困ったときにはまずやってみましょう.

この手法はprintfデバッグと呼ばれることがあります.printfというのはC言語で画面表示を行うための関数です.

また,実行中にエラーが起きることもあります.Javaでは,実行中のエラーは例外(Exception)という形で報告されます.この場合は,その例外の種類を把握し,なぜ例外が発生したのかを突き止める必要があります.


フラグとboolean型

キーボードから整数 n を入力し,n が素数か否かを判定するプログラム Prime を作ってみましょう. nが素数ならば,nは 2 〜 (n-1) のどの自然数でも割り切れないことを利用します.

本当は2〜\sqrt nの間の自然数で割り切れなければ十分ですが,ここでは簡単のためこうしています.

考え方としては,for文を使って変数(iとします)に2から(n-1)までの値を順番に入れてチェックします. どれかの数で n を割りきれたら(nをiで割った余りが0になるiが存在すれば),素数ではありません. 逆にどの数でも割り切れなかったらnは素数です.

つまり,for文の繰り返しの中で,割り切る数が存在したかどうかを覚えておけば良いわけです.

このように,何らかの条件判定結果を覚えておくための変数のことを,プログラミングの世界ではフラグ(flag)とかフラグ変数と呼び,非常によく使われます.

さて,「割りきる数が存在したか」というのは存在したか,存在しなかったの2通りしかありません.

これを,例えばint型の変数を使って0か1かなどで表すこともできますが,Javaではこういうことを表すのに都合が良い boolean型という基本型があります.boolean型の変数には,trueかfalseしか入れることができません.

実は,if文やwhile文等の条件に書いていた真偽式(a == 0 等)は,boolean型で結果を返す式だったのです.また,true や false というのはboolean型の定数になります.

せっかくなので,以前示したJavaの基本型の表を更新しておきましょう.

Javaの基本型(一部)
種別大きさ定数の表記法値の範囲
整数int32ビット10,-499-2147483648〜2147483647
倍長整数long64ビット10L,-499L-9223372036854775808 〜 9223372036854775807
実数float32ビット3.1415f,1.3e32f1.4e-45(最小) 〜 3.4028235e38(最大)

(それぞれ1.4\times10^{-45}, 3.4028235\times 10^{38}という意味)

倍長実数double64ビット3.1415,1.3e-324.9e-324(最小) 〜 1.7976931348623157e308(最大)
真偽値booleantrue, falsetrue, falseの2つの値しかありません

フラグがtrueのときフラグが立っているといいます(falseのときフラグが寝ているという表現が使われることもあります).


プログラム例(素数判定)

// 素数判定
public class Prime {
    public static void main(String[] args) {
        System.out.print("整数を入力して下さい: ");
        int n = Keyboard.intValue();
 
        // 素数かどうかを覚えておくフラグ変数.最初は true にしておき,
        // 割り切る数を見つけたら false にする
        boolean isPrime = true;
 
        // 2〜(n-1)までの間に割りきる数があったら素数ではない
        for (int i = 2; i < n; i++) {
            if (n % i == 0) {           // 割りきれる?
                isPrime = false;        // 素数ではなかった
                // 素数でないことがわかったので,これ以上繰り返しても無駄.
                break;
            }
        }
 
        // 結果の表示
        // 一度も割りきれなかったら,
        // isPrime == true のままここに到達する!
        if (isPrime) {
            System.out.println(n + "は素数です");
        } else {
            System.out.println(n + "は素数ではありません");
        }
    }
}

フラグとしてisPrime という boolean型の変数を使っています.

if (isPrime) と書いているところに注目してください.isPrime は trueかfalseが入っている変数なので, このように書くことができます(if (isPrime == true) と書く必要はありません). isPrimeにtrueが入っているときにif文の本体が実行されます.

if (isPrime == true) と書かないと気持ち悪いという人は,if ((isPrime == true) == true) と書かないと気持ち悪いはずです(よね?).

また,フラグが false ならばある処理をしたいという場合,if (!isPrime) のように書きます.


また,素数でないことがわかったらこれ以上ループを繰り返すのは無駄なので break 文でループを打ち切っています.

フラグ変数の変数名には,

  • is〜 (〜かどうか.例: isPrime)
  • 過去分詞形 (例: found, configured, ordered)

がよく使われます.


演習(モンテカルロ法)

n個の大きさの配列の各要素に乱数で0以上1未満の実数値を入れておきます. このとき,配列が昇順に並んでいる確率を求めましょう.

解析的に解く方法もありますが,ここではモンテカルロ法でやってみます.モンテカルロ法というのは,乱数を用いて近似解を求める方法の総称です.

十分多くの回数(例えば10万回)試行して,昇順に並んだ回数を数えます. 昇順に並んだ回数を試行の回数で割れば,近似的に確率が求まるというわけです.

以下のプログラム OrderedProbability は未完成です. !!!! fill in here !!!! の部分を付け加えてプログラムを完成させてください. 昇順に並んでいるかどうかのチェックのためにフラグ変数を使います.

/*
 * 乱数で作った配列が昇順に並ぶ確率を調べるプログラム
 */
public class OrderedProbability {
    public static void main(String[] args) {
        int n = 4;
        int ntrial= 100000;        // 試行の回数
        int nordered = 0;        // 昇順になっていた回数
        // ntrial回試行する
        for (int i = 0; i < ntrial; i++) {
            // 大きさnの配列に乱数で0から1までの値を代入
            double[] data = new double[n];
            for (int j = 0; j < data.length; j++) {
                data[j] = Math.random();
            }
            
            // 配列の先頭から,昇順に並んでいるかチェックし,昇順になっていたら nordered の値を +1
            // !!!! fill in here !!!!
        }
        System.out.println("昇順になった回数: " + nordered);
        System.out.println("昇順になる確率は " + (double)nordered / ntrial);
    }
}

なお,最後に nordered を (double) でキャストして実数型に変換しているのは,整数の割り算として計算されてしまうのを防ぐためです.よく分からない人は定数の項とキャスト演算子の項を参照して下さい.

完成したら,n の値を変化させたときにどのように確率が変化するかを観察してみましょう. (n = 4 の場合,確率は約0.0417になるはずです).

また,解析的に解いた場合どうなるかも考えてみましょう.

演習(モンテカルロ法)の回答例


メソッド

だんだんプログラムが複雑になってきました. 複雑になっている原因の一つは,全ての処理を一ヶ所(mainメソッド)で書いているからです.

プログラムを幾つかのメソッドに分割することで,プログラム全体の見通しを良くし,より簡単に書けるようになります(ちゃんと理解すればですが...).ここではメソッドを用いたプログラミングを学びましょう.

メソッドとは,いまのところプログラムで何らかのまとまった処理を行う単位だと思っておいて下さい.

  • クラスの中で自分の好きなメソッドを(複数)定義できます
  • メソッド(mainメソッドを含む)から,メソッドを呼び出すことができます
  • メソッドでは,何らかの処理を行い,(必要があれば)処理結果を呼び出し元のメソッドに返すことができます

「メソッド(method)」はオブジェクト指向プログラミングで使われる用語です. C言語などの手続き型プログラミング言語では 関数(function)手続き(procedure)などと呼ばれます.

手続き型プログラミングでは基本的に関数(あるいは手続き)より大きい構造はないので,巨大なプログラムは多数の関数(や手続き)の集まりとして作成します.

オブジェクト指向プログラミングでは,メソッドより大きい構造としてクラスがあり(詳しくは後日),プログラムは1つ以上のクラスから構成されます.それぞれのクラスはメソッド(と,まだ説明していませんがデータ)の集まりです.

まずはメソッドを用いたプログラムの例を見てみましょう. 以下は先ほどの素数判定のプログラムをメソッドを使って書き直したものです.

0001:// 素数判定
0002:public class Prime2 {
0003:    public static void main(String[] args) {
0004:        System.out.print("整数を入力して下さい: ");
0005:        int num = Keyboard.intValue();
0006:
0007:        // メソッド isPrime を呼び出し,結果を変数 p に代入
0008:        boolean p = isPrime(num);
0009:        if (p) {
0010:            System.out.println(num + "は素数です");
0011:        } else {
0012:            System.out.println(num + "は素数ではありません");
0013:        }
0014:    }
0015:    
0016:    /*
0017:     * nが素数かどうかを判定するメソッド.
0018:     * 返り値はboolean型で,nが素数の場合にtrueを返し,そうでない場合はfalseを返す.
0019:     */
0020:    private static boolean isPrime(int n) {
0021:        // 素数かどうかを覚えておくフラグ変数.最初は true にしておき,
0022:        // 割り切る数を見つけたら false にする
0023:        boolean isprime = true;
0024:
0025:        // 2〜(n-1)までの間に割りきる数があったら素数ではない
0026:        for (int i = 2; i < n; i++) {
0027:            if (n % i == 0) {           // 割りきれる?
0028:                isprime = false;        // 素数ではなかった
0029:                // 素数でないことがわかったので,これ以上繰り返しても無駄.
0030:                break;
0031:            }
0032:        }
0033:        return isprime;
0034:    }
0035:}
0036:

注意深く見てください.

  • 素数を判定する部分を isPrime というメソッドに切り出し
  • mainメソッドでは isPrime メソッドを呼び出し,その結果に応じて画面表示
  • isPrimeメソッドは与えられた値が素数かどうかを判定してtrueかfalseで結果を返す


メソッド分割の利点

このようにプログラムを複数のメソッドに分割して記述すると,以下のような利点があります.

  • ある処理を行うメソッドを一度書いてしまえば,後はそのメソッドの使い方さえ知っていればその処理を実行できる.

内部の細かい処理のことは知らなくて良い(抽象化).

  • 一つ一つのメソッドの構造が簡単になるため,コーディングや保守が容易になる
  • メソッド毎にテストすることができるので,デバッグが容易になる
  • メソッド単位で使い回すことによって,他のプログラムを作る際の生産性が向上する

では,上のプログラムを詳しく見ていきましょう.

mainメソッド

mainメソッドでは,新しく書いたisPrimeメソッドを呼び出しています.呼び出す際,素数かどうかを判定して欲しい値 num を与えています.

今まで書いてきた Math.random() や Math.abs(..) も実はメソッドの呼び出しです.

isPrimeメソッドを呼び出すと,mainメソッドの実行は一旦中断し,isPrimeメソッドの実行が始まります.isPrimeメソッドの実行が終了したら,mainメソッドの実行が再開されます.isPrimeメソッド実行中も,mainメソッドは終了したわけではありませんから,mainメソッドの中の変数の値はちゃんと保持されています.

isPrimeメソッドは結果(素数か否か)を返すので,pという変数に代入しています.

isPrimeメソッド

20行〜34行がメソッドisPrimeの定義です.このメソッドは与えられた値が素数かどうかを判定します.

まず,最初の private static はおまじないだと思っておいて下さい(またもや!).mainメソッドはpublicでしたが, しばらくの間,mainメソッド以外のメソッドはprivateとしておきます.

次の boolean はこのメソッドが返す値の型です.判定結果はtrue(素数)かfalse(素数でない)で表せるので, booleanを選んでいます.

その次の isPrime はメソッド名です.

その次の (int n) は引数(parameter)の型と名前を表しています. int型の値を1つ受け取り,与えられた値は n という変数に入れておくということを表しています.

あとの部分は今までと同様に n が素数かどうかを判定しています.

混乱を避けるため変数名を isPrime から isprime に変えていますが,本当は変更する必要はありません.

最後に return 文があります.return 文は値を呼び出し側に返すための文です.ここでは isprime 変数の値を返しています. return文を実行すると,そのメソッドの実行は終了し,メソッドを呼び出した側の実行が再開されます.

メソッドの定義

メソッドは次のように書きます.クラスの中にしか書けません.また,他のメソッドの中にも書けません.

public class クラス名 {
  private static 型名 メソッド名1(引数の型 引数名, 引数の型 引数名...) {
    メソッド1の中身
  }
  private static 型名 メソッド名2(引数の型 引数名, 引数の型 引数名...) {
    メソッド2の中身
  }
}
型名
メソッドが返す値の型(int, boolean, doubleなど)を書きます.
返り値が不要な場合(ある処理を実行するだけで,結果が不要な場合)は void と書きます.
メソッド名
メソッド名は変数名と同じように好きな名前を付けることができます.
変数名と同様に,メソッド名も小文字から始める習慣なので,従っておきましょう.
引数
引数はカンマで区切って複数並べることができます.また,引数がない場合もカッコは必要です.

メソッドの中身には,今までmainメソッドで書いてきた構文の全てを利用できます. (mainメソッドは,最初に実行されるメソッドという点が特別なだけです.)

1つのメソッドでは,意味のあるまとまった処理を行うようにします. 1つのメソッドの中であまり関係ない複数の処理を行うべきではありません.

以下にメソッドの例を示します.

 
public class MethodSamples {
    // 直角三角形の2辺の長さを与えて斜辺の長さを計算するメソッド
    private static double hypotenuse(double a, double b) {
        // Math.sqrt は平方根を計算するメソッド
        return Math.sqrt(a * a + b * b);
    }
    
    // BMIを計算するメソッド
    // heightはセンチメートル,weightはキログラム単位で与える
    private static double calcBMI(double height, double weight) {
        double h = height / 100.0;
        return weight / (h * h);
    }
    
    // n個の*を表示し,改行するメソッド
    // 返り値は不要(存在しない)ので void になっている
    private static void printBar(int n) {
        for (int i = 0; i < n; i++) {
            System.out.print("*");
        }
        System.out.println();
    }
 
    // 乱数によって今日の運勢を表示するメソッド
    // 引数はないので () になっている
    private static void printLuckiness() {
        double r = Math.random();
        if (r > 0.8) {
            System.out.println("Very Lucky!");
        } else if (r > 0.5) {
            System.out.println("Lucky");
        } else {
            System.out.println("Not Lucky");
        }
    }
}

各メソッドの直前には,そのメソッドの動作や引数,返り値の説明をコメントとして書いておきましょう. 後から読みやすくなるのはもちろん,コーディングの段階でも自分が書くべき内容が明確になります.

実は,Javaではメソッドのコメントはある程度決められた形式で書いておく約束になっています. Eclipseでmainメソッドを自動生成すると,メソッドの先頭に

/**
 * @param args
 */

というコメントが入りますが,これがその形式で,ドキュメンテーションコメントと呼ばれます. 詳しくは http://ja.wikipedia.org/wiki/Javadoc などを参照して下さい

return文

voidでないメソッドでは,必ず return 文が必要です(無いとコンパイルエラー).

return 式; 

のように書きます.式の型は,メソッドの型と一致している必要があります.

1つのメソッドに複数のreturn文があっても構いません.最初に実行したreturn文によってメソッドの実行は終了します. このため,上の isPrimeメソッドは次のようにも書けます.

private static boolean isPrime(int n) {
  // 2〜(n-1)までの間に割りきる数があったら素数ではない
  for (int i = 2; i < n; i++) {
    if (n % i == 0) {           // 割りきれる?
      // 素数でないことがわかったので,falseを返して終了.
      return false;             // 素数ではなかった
    }
  }
  return true; // ここに到達したということは素数ということ
}

voidメソッドでは,return文は無くても構いません.return文が無い場合は,メソッドの最後に到達したらメソッドの実行は終了します.return文を書く場合は単に

return; 

と書きます.

return文に関する注意

以下のようにreturn文の後に文を書くとコンパイルエラーになります.

return false;
System.out.println("..");  // エラー! この文は決して実行されない

また,voidでないメソッドでは,実行される可能性のある全てのパス(プログラムが実行される通り道)で return 文が必要です.

例えば,以下のプログラムはコンパイルエラーになります(n != 0 の場合に実行する return 文がない).

private static boolean ret1(int n) {
   if (n == 0) {
     return true;
   }
}

次のように書けば大丈夫です.

private static boolean ret2(int n) {
   if (n == 0) {
     return true;
   }
   return false;
}

private static boolean ret3(int n) {
   if (n == 0) {
     return true;
   } else {
     return false;
   }
}

メソッドの呼び出し

定義したメソッドを呼び出すには,以下のように書きます.

メソッド名(引数, 引数, ...);  // 引数がある場合
メソッド名(); // 引数がない場合

メソッドの定義には引数の型を書く必要がありますが,呼び出す側では必要ありません.

boolean p = isPrime(int num); // 間違い(intは不要)

メソッドが値を返す場合(つまりvoidではない場合),メソッド呼び出しは式として扱えます (return文で返された値が式の値になります).

class Foo {
  private static void main(String[] args) {
     int f = add(10, 20) + add(30, 40);   // メソッド呼び出し
     // ↓これもメソッド呼び出し
     if (isZero(f)) {
       System.out.println("...");
     }
     System.out.println("1 + 2 = " + add(1, 2)); // メソッド呼び出し
  }

  // 2つの引数を加算した値を返す関数
  private static int add(int a, int b) {
    return a + b;
  }

  // 引数が0ならばtrueを返す関数
  private static boolean isZero(int n) {
    // n == 0 なら true, 成立しなければ false 
    return n == 0;
  }
}

ですから,上の素数判定プログラムのmainメソッドは(いちいちisPrimeメソッドの結果を変数に代入せずに) 次のようにも書けます.

public static void main(String[] args) {
  System.out.print("整数を入力して下さい: ");
  int num = Keyboard.intValue();

  if (isPrime(num)) {
     System.out.println(num + "は素数です");
  } else {
     System.out.println(num + "は素数ではありません");
  }
}

メソッドfooがメソッドbarを呼び出した場合,

  • barからfooの変数を直接みることはできません.fooから受け取れる情報は,barに与えられた引数だけです.

ですから,あるメソッドが必要な情報は全て引数で受け取る必要があります.

  • 同様に,メソッドfooからはメソッドbarの変数は見えません.barから受け取れる情報はreturn文で渡された値だけです.

ですから,複数の値を返すメソッドを書くには工夫が必要です.

値渡し

Javaでは,メソッドを呼び出す際,引数の値はコピーされて渡されることになっています(値渡し(call by value)と言います).

次のプログラムでは,mainからbarを呼び出す際,mainの変数nの値(10)が,barの引数のnにコピーされます. barの中で引数の値を変更していますが,mainの中の変数nの値にはまったく影響ありません.

class Bar {
  private static void main(String[] args) {
    int n = 10;
    bar(n);
    System.out.println(n);   // 10が表示される
  }

  // この引数の n と,mainの中の n はまったく別の変数
  // ここでは同じ n という名前になっているが,変えてもよい
  private static void bar(int n) {
    n++;
  }
}

演習(メソッド1)

以下のプログラム MethodExercise の不足部分(!!!! fill in here !!!! の部分)を埋めよ.

// メソッド定義の練習
public class MethodExercise {
    public static void main(String[] args) {
        System.out.println("sum from 0 to 10 = " + sum(10));
        System.out.println("sum from 0 to 100 = " + sum(100));
        System.out.println("sum from 0 to 1000 = " + sum(1000));
        for (int i = 0; i < 10; i++) {
            if (isEven(i)) {
                System.out.println(i + "は偶数です");
            } else {
                System.out.println(i + "は奇数です");
            }
        }
    }
    
    // 0 から n までの合計を計算して返すメソッド sum
    private static int sum(int n) {
        // !!!! fill in here !!!!
    }
 
    // 引数の値が偶数ならtrueを,そうでなければfalseを返すメソッド isEven
    private static ...
    // !!!! fill in here !!!!
}

演習(メソッド1)の回答例

演習(メソッド2)

以下のプログラム MethodExercise2 の不足部分を埋めよ. 最大公約数を求めるためにはユークリッドの互除法を使えばよい.

// メソッド定義の練習
public class MethodExercise2 {
    public static void main(String[] args) {
        System.out.println("GCD(24, 8) = " + gcd(24, 8));
        System.out.println("GCD(144, 84) = " + gcd(144, 84));
    }    
 
    // 2つの引数の最大公約数を求めるメソッド gcd
    private static ...
    // !!!! fill in here !!!!
}

演習(メソッド2)の回答例

課題5-1(メソッド1)

以下のメソッドを含むプログラム Methods を書きなさい.

  • 円の半径(double)を引数として受け取り,円の面積を返す(returnする)メソッド circleArea
  • 球の半径(double)を引数として受け取り,球の体積を返すメソッド sphereVolume
  • 3つの数(int)を引数として受け取り,その最大値を返すメソッド max3

円周率(π) は Math.PI を使います(以前の演習では3.14を使いましたが,Math.PIのほうがより精度があります).Math.PI はメソッドではなく定数なので以下のように使います.

// 円周 = 2 * 半径 * π
double perimeter = 2 * radius * Math.PI;

mainメソッドは以下のものを使うこと.

public static void main(String[] args) {
  System.out.println(max3(1, 2, 3));
  System.out.println(max3(1, 3, 2));
  System.out.println(max3(2, 1, 3));
  System.out.println(max3(2, 3, 1));
  System.out.println(max3(3, 1, 2));
  System.out.println(max3(3, 2, 1));

  for (double r = 1.0; r <= 10.0; r += 1) {
    System.out.println("r = " + r + ", area = " + circleArea(r)
      + ", volume = " + sphereVolume(r)); 
  }
}

最初のmax3の呼び出しの羅列は,max3メソッドが正確に動作しているかをテストするためのものです (3つの数の全ての並べ方を試しています).

以下の出力が得られるはずです.

3
3
3
3
3
3
r = 1.0, area = 3.141592653589793, volume = 4.1887902047863905
r = 2.0, area = 12.566370614359172, volume = 33.510321638291124
r = 3.0, area = 28.274333882308138, volume = 113.09733552923255
r = 4.0, area = 50.26548245743669, volume = 268.082573106329
r = 5.0, area = 78.53981633974483, volume = 523.5987755982989
r = 6.0, area = 113.09733552923255, volume = 904.7786842338604
r = 7.0, area = 153.93804002589985, volume = 1436.755040241732
r = 8.0, area = 201.06192982974676, volume = 2144.660584850632
r = 9.0, area = 254.46900494077323, volume = 3053.628059289279
r = 10.0, area = 314.1592653589793, volume = 4188.790204786391

課題5-2(メソッド2)

演習(モンテカルロ法)の回答例では試行するためのfor文の中に, 試行本体(11行目から24行目まで.配列に乱数をいれて,昇順かどうかをチェック)が入っているため,若干構造が複雑です. 試行本体の部分を別のメソッド isOrdered にして,構造を単純化したプログラム OrderedProbability2 を作ってください.

isOrderedメソッドは,配列が昇順だったら true を返すようにします. 引数に何が必要かはよく考えてください.

Copyright (C) 2002-2015 Kota Abe, Osaka City University

ナビゲーション