Javaプログラムで予期せぬ動作が発生する理由

このQ&Aのポイント
  • Javaの再帰的なプログラムで予期しない結果が生じています。カウンターの値が予想と異なる出力がされます。
  • ソースコードを見ても、なぜこのような動作が起こるのか理解できません。どうして予測と異なる結果になるのか教えてください。
  • 質問者は期待している動作は次のようなものですが、実際には異なる結果が得られます。なぜこのような動作になるのでしょうか?
回答を見る
  • ベストアンサー

【Java】再帰的なプログラムで予期せぬ動作をする

【環境】 java version "1.6.0_31" Java(TM) SE Runtime Environment (build 1.6.0_31-b05) Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01, mixed mode) ---------------------------------------------------------------- 以下のようなプログラムを書いたのですが、自分が予測していた動作と異なり、困っています。 私は -------------Start------------- カウント----------->0 2で割り切れちゃった----------->0 カウント----------->1 2で割り切れなかった----------->1 カウント----------->2 2で割り切れちゃった----------->2 カウント----------->3 2で割り切れなかった----------->3 カウント----------->4 2で割り切れちゃった----------->4 -------------End------------- という動作を期待していたのですが、実際は -------------Start------------- カウント----------->0 2で割り切れちゃった----------->0 カウント----------->1 2で割り切れなかった----------->1 カウント----------->2 2で割り切れちゃった----------->2 カウント----------->3 2で割り切れなかった----------->3 カウント----------->4 2で割り切れちゃった----------->4 2で割り切れなかった----------->5 2で割り切れなかった----------->6 2で割り切れなかった----------->7 -------------End------------- となってしまいました。考えても、どうしてこのように動作するのか理解できなかったので、こちらに質問しました。よろしくお願いします(ソースコードは下です)。 -----------------以下、ソースコード----------------- Main.java public class Main {     public static void main(String args[])     {         Saiki saiki = new Saiki();         System.out.println("-------------Start-------------");         saiki.playSaiki();         System.out.println("-------------End---------------");     } } Saiki.java public class Saiki {   private int counter;   Saiki()   {     counter = 0;   }   public void playSaiki()   {     if(counter < 5)     {       System.out.println("カウント----------->" + counter);       if(counter%2 == 0)       {         System.out.println("2で割り切れちゃった----------->" + counter);         counter++;         playSaiki();       }       System.out.println("2で割り切れなかった----------->" + counter);       counter++;       playSaiki();     }   } }

  • Java
  • 回答数3
  • ありがとう数3

質問者が選んだベストアンサー

  • ベストアンサー
  • root139
  • ベストアンサー率60% (488/809)
回答No.2

既に回答が付いていますが、2で割り切れる場合もifブロックの中での再起処理が終わった後にその下の処理が続けて行われるからですね。 下記の様にいくつか簡単な修正方法がありますが、修正後のソースから考えた方が分かりやすいかもしれません。 a. 2で割り切れる場合のifブロックの最後でメソッドを抜ける 例) ------------------------------------ if (counter %2 == 0) {   System.out.println("2で割り切れちゃった----------->" + counter);   counter++;   playSaiki();   return; // この行を追加 } ---------------------------------------- b. ifで場合分けするのは、メッセージの表示部分だけにする 例) ------------------------------------ if (counter %2 == 0) {   System.out.println("2で割り切れちゃった----------->" + counter); } else {   System.out.println("2で割り切れなかった----------->" + counter); } counter++; playSaiki(); ----------------------------------------

kuma2sika
質問者

お礼

回答ありがとうございます。 疑問に答えるだけでなく、改善策まで書いてくれるなんて...ほんとに感謝です。 修正するなら、2つ目のコードの方が無駄が少なくなくなりそうなので、それでやってみます。

その他の回答 (2)

回答No.3

ちょっと考えればわかるけど、 以下は playSaiki()の先頭に来たときのスタックトレースもどきです。 最初ぐんぐん潜っていって、戻り際にも3個出力するので、 0~7が出力されるのは明らかでしょう。 playSaikiは pSと略記してます。 pS() counter==0 pS() 割切->pS() counter==1 pS() 割切->pS() 割切ず->pS() counter==2 pS() 割切->pS() 割切ず->pS() 割切->pS() counter==3 pS() 割切->pS() 割切ず->pS() 割切->pS() 割切ず->pS()counter==4 pS() 割切->pS() 割切ず->pS() 割切->pS() 割切ず->pS() 割切->pS() counter==5 pS() 割切->pS() 割切ず->pS() 割切->pS() 割切ず->pS() 割切ず->pS() counter==6 pS() 割切->pS() 割切ず->pS() 割切ず->pS() couter==7 pS() 割切ず->pS() counter==8

kuma2sika
質問者

お礼

スタックトレースもどき、ありがとうございます。 おかげで、視覚的にも問題を理解することができました。 今度またこのように再帰的にプログラミングする際は、自分でしっかり流れを追えるように、スタックトレースみたいなものを書いてみます。

  • OKWavex
  • ベストアンサー率22% (1222/5383)
回答No.1

偶数で「2で割り切れちゃった」を表示してplaySaiki();を呼んだあとで必ず呼び元に戻って1を加えた奇数で「2で割り切れなかった」を表示するのだから、再帰的呼び出しで4まで処理したあとは呼び元に戻りながら偶数である 4 2 0 の際には常に1を加えた 5 6 7 で 「2で割り切れなかった」を表示しているだけでしょう

kuma2sika
質問者

お礼

回答ありがとうございます。 なるほど、よび元にもどっていくと、確かに4,2,0のときの下の処理を通ってしまいますね。 理解しました、ありがとうございます。

関連するQ&A

  • JAVAのプログラムについて

    独学でJAVAを勉強中なのですが、 import java.io.*; public class ExserciseD5L3_2{ public static void main(String args[]){ BufferedReader br = new BufferedReader(newInputStreamReader(System.in),1); try{ System.out.println("■■■計算クイズ■■■"); System.out.println("計算してください。"); String Que[] = {"10×50=?","21-7=?","360÷6=?"}; int Ans[] = {500,14,60}; int counter; for(counter = 0; counter <=2; counter++ ){ System.out.println(Que[counter]); System.out.println("答えは?"); String str = br.readLine(); int i = Integer.parseInt(str); if(i == Ans[counter]){ System.out.println("おめでとう!大当たりです。"); } else{ System.out.println("残念!答えは"+Ans[counter]+"です。"); } } } catch(IOException e){ System.out.println("IOエラーが発生しました。"); } catch(NumberFormatException ne){ System.out.println("入力された数値が正しくないようです。"); } } } これを実行すると ■■■計算クイズ■■■ 計算してください。 10×50=? 答えは? 500 おめでとう!大当たりです。 21-7=? 答えは? 14 おめでとう!大当たりです。 360÷6=? 答えは? 60 おめでとう!大当たりです。 となるのですが、これに おめでとう!正解数は3つです。とか正解数は2つですなどのように 正解数も出るようにするにはどのようにしたらいいのでしょうか?

    • ベストアンサー
    • Java
  • Java何故エラーになるのですか?

    public class Sample3_1 { public static void notMain(String[] args) { System.out.println("not main"); } public static void main (String[] args) { System.out.println("こんにちは、Java"); System.out.println("私の名前は、コウゾウです。"); } } Exception in thread "main" java.lang.Error: Unresolved compilation problems: トークン "Invalid Character" に構文エラーがあります。このトークンを削除してください 構文エラーがあります。"}" を挿入して ClassBody を完了してください at Sample3_1.main(Sample3_1.java:10) "}" を挿入して」と書かれてますが、 "}" には間違いなさそうです。 どなたか解説のほどを宜しくお願い致します。

    • ベストアンサー
    • Java
  • Java初心者です

    次のようなプログラムを作りました interface Vehicle{ void drive(); } abstract class Animal{ abstract void show(); } class Lion extends Animal{ public void show(){ System.out.println("ライオンです"); } } class Horse extends Animal implements Vehicle{ public void show(){ System.out.println("馬です"); } public void drive(){ System.out.println("乗り物として使えます"); } } class Elephant extends Animal implements Vehicle{ public void show(){ System.out.println("象です"); } public void drive(){ System.out.println("乗り物として使えます"); } } class Bear extends Animal{ public void show(){ System.out.println("熊です"); } } class twentyeight{ public static void main(String args[]){ Animal an[]; an = new Animal[4]; an[0] = new Lion(); an[1] = new Horse(); an[2] = new Elephant(); an[3] = new Bear(); for(int i = 0;i < an.length;i++){ if(an[i] instanceof Vehicle){ an[i].drive();//ここにエラーが出る } else{ an[i].show(); } } } } インターフェイスを実装しているオブジェクトを判別し、そのdriveメソッドの呼び出しをしたいのですができません、どなたか教えてください。

    • ベストアンサー
    • Java
  • [JAVA]エラーが解決できません。

    JAVAのエラーが解決できません。 ?,[,],(,)をreplaceAllを用いて置換したいと下記のようなプログラムを書いたのですが正常に動作しません。 また、Unicodeに置き換えても見たのですが、うまく動作しませんでした。 そして、排出されたエラーは下記の通りです。 文中にある、?などの記号を置換したいのですが何か良い方法は無いでしょうか? 何卒、ご教授、宜しくお願い致します。 テストコード>>> class Test { public static void main(String args[]) { System.out.println(args[0].replaceAll("?", "置換?")); System.out.println(args[0].replaceAll("[", "置換[")); System.out.println(args[0].replaceAll("(", "置換(")); } } エラー>>> Exception in thread "main" java.util.regex.PatternSyntaxException: Dangling meta character '?' near index 0 ? ^ at java.util.regex.Pattern.error(Unknown Source) at java.util.regex.Pattern.sequence(Unknown Source) at java.util.regex.Pattern.expr(Unknown Source) at java.util.regex.Pattern.compile(Unknown Source) at java.util.regex.Pattern.<init>(Unknown Source) at java.util.regex.Pattern.compile(Unknown Source) at java.lang.String.replaceAll(Unknown Source) at Test.main(Test.java:9)

    • ベストアンサー
    • Java
  • Javaのプログラムで文字が出力できません

    Javaについて勉強中です。 Java SE Development Kit 7u6というものをoracleのサイトがらダウンロードし、インストールしました。 >javac -versionと入力すると javac 1.7.0_06 >java -versionだと java version "1.7.0_06" Java(TM) SE Runtime Environment (build 1.7.0_06-b24) Java HotSpot(TM) Client VM (build 23.2-b09, mixed mode, sharing) と表示されるのでインストールはうまくできていると思うのですが、 class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!!"); } } をtest.javaで保存し、javac test.javaと入力してもコマンドプロンプトには何も表示されません。 なぜでしょうか?教えてくださいm(_ _)m

    • ベストアンサー
    • Java
  • プログラムの組み合わせ方

    java初心者です。 2つのプログラムを組み合わせて1つのプログラムにしたいんですが、イマイチ分かりません。 このプログラムと、 import java.io.*; class Hello2 {  public static void main(String[] args) throws IOException {   System.out.println("名前を入力してください");   BufferedReader br = new BufferedReader(new InputStreamReader(System.in));   String str = br.readLine();   System.out.println(str + "さん、こんにちは!");  } } このプログラムを、 import java.io.*; class Sanbai{  public static void main(String[] args) throws IOException {   System.out.println("好きな数を入力してください");   BufferedReader br = new BufferedReader(new InputStreamReader(System.in));   String str = br.readLine();   int x;   x = Integer.parseInt(str);   System.out.println("あなたの入力した数:" + x);   System.out.println("その3倍の数:" + (x*3));  } } 組み合わせて1つのプログラムにするにはどうしたらいいですか?(>_<)

    • ベストアンサー
    • Java
  • 他ファイルで定義された自作例外の受け方

    他ファイルで定義された自作例外の投げ方と受け方を 教えて下さい。 // ------ Class.java ---> import java.io.*; public class Class1 {  static Class2 oClass2;  public static void main (String[] args)  {   try {    oClass2 = new Class2();    System.out.println("main");    oClass2.vfFunc();   } catch (MyException e) {    System.out.println("catch MyException");   } catch (Exception e) {    System.out.println("catch Exception");   }  } } // <------ Class1.java --- // ------ Class2.java ---> import java.io.*; public class Class2 {  public Class2()  {   System.out.println("Class2コンストラクタ");  }  public void vfFunc()  {   System.out.println("Class2 vfFunc");   throw new MyException();  }  public class MyException extends Exception  {   public MyException()   {    System.out.println("MyException");   }  } } //<------ Class2.java ---

    • ベストアンサー
    • Java
  • 可変長引数リストを用いたプログラムがコンパイル不可

    可変長引数リストを用いたプログラムがコンパイルできません。 【Sample.javaの内容】 class MyClass extends java.lang.Object { void meth(String ... s) { for (int i=0; i<s.length; i++) { System.out.println(s[i] + " : "); } System.out.println(); } } public class Sample extends java.lang.Object { public static void main (String args[]) { MyClass mc = new MyClass(); mc.meth(); mc.meth("what", "is", "this"); mc.meth("this", "is", "an", "example"); } } 【普通にjavacでコンパイル】 >javac Sample.java Sample.java:2: <identifier> がありません。 void meth(String ... s) { ^ Sample.java:8: <identifier> がありません。 } ^ エラー 2 個 【-sourceオプションをつけてコンパイル】 >javac -source 1.5 Sample.java javac: 1.5 は無効なソースバージョンです。 (以下javacのusageが延々と表示される) ※-version 1.6でも同様の結果でした。 【バージョン情報】 C:\>java -version java version "1.6.0_03" Java(TM) SE Runtime Environment (build 1.6.0_03-b05) Java HotSpot(TM) Client VM (build 1.6.0_03-b05, mixed mode, sharing)

    • ベストアンサー
    • Java
  • [javaプログラムについて] うまく動きません。

    import java.*; class Test { void main() { Th th1 = new Th("01スレッド"); Th th2 = new Th("02スレッド"); th1.start(); th2.start(); } } class Th extends Thread { void run(String pri) { for (int i=0;i<5;i++) { System.out.println(pri + " : " + i); } } } スレッドの使い方がいまいちよく分かりません。 どこが間違っているのか教えてください。 宜しくお願い致します。

    • ベストアンサー
    • Java
  • かなり初歩的なプログラミング(Java)についての質問です。

    かなり初歩的なプログラミング(Java)についての質問です。 お恥ずかしながら、かなり初歩ではありますが躓いております・・・。 import java.util.Scanner; class OddEven{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); System.out.println("整数を入力してください。"); String str1=sc.next(); int i = Integer.parseInt(str1); if(i%2==0){ System.out.println(i+"は偶数です。"); } else { System.out.println(i+"は奇数です。"); } } } } 以上のような内容なのですがコンパイルしようとすると 「OddEven.java:21: class、interface、または enum がありません。」 といったエラーがでます。 文法などは間違ってない!・・・と思うのですが・・・。 ご指摘していただきたいです。 あともう一つ質問です。じつはこっちが聞きたかったり・・・。 上記のソースコードを見ていただければわかると思いますが 「import java.util.Scanner;」 と最初の部分に記述していますが、これは授業で教師が教えてくれたもので手持ちの参考書では 「import java.io*;」 と記述されています。(もしかして今回のエラーもここから・・・?) この二つの違いは・・・聞いても正直分かりそうにはないので どっちを使っていけばいいか、などを教えていただきたいです。

    • ベストアンサー
    • Java

専門家に質問してみよう