• ベストアンサー

再帰メソッドについて

いつもお世話になっております。 再帰メソッドについて質問があります。 後述のプログラム実行後、以下の結果が 得られました。 ********** depth = [0] , value = [0] ********** depth = [1] , value = [1] ********** depth = [2] , value = [2] ########## depth = [2] , value = [3] …★ ########## depth = [1] , value = [3] recursionMethod() は3回呼ばれました。 (以下、ソース) public class RecursionTest { private static final int MAX_DEPTH = 2; private static int cnt = 0; private int val =0; public static void main(String[] args) { // 再帰メソッドの呼び出し RecursionTest own = new RecursionTest(); own.recursionMethod(0); System.out.println("\n recursionMethod() は" + cnt + "回呼ばれました。"); } private void recursionMethod(int depth) { // 再帰メソッドが呼ばれた回数をカウントする cnt++; System.out.println("********** depth = [" + depth + "] , value = [" + val + "]"); val++; // パラメータ depth が MAX値に到達したら、return する if (depth++ >= MAX_DEPTH) { return; } // 自分自身の呼び出し recursionMethod(depth); System.out.println("########## depth = [" + depth + "] , value = [" + val + "]"); } } 実行結果の★以降が得られる理由が全く分かりません。 ・return で main() に戻るはずなのになぜ「###~」のログが出力されるのか ・depth の値がなぜデクリメントされるのか (誰がデクリメントの処理をしているのか) そもそも上記の結果を返すことが「メソッドの再帰的呼び出し」ということなのでしょうか? ご教授頂ける方がいらっしゃいましたら、宜しくお願い致します。

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

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

  • ベストアンサー
回答No.4

daisy_kさん、こんにちは betagamma さんが回答された通り、returnするからと言って、mainメソッドに戻るわけではありません。そのメソッドを呼び出したメソッドに戻るだけです。 またデクリメント処理についてですが、結論から言うと行われているわけではありません。 Javaを含め、メソッドの再起呼び出しをサポートしている言語では、メソッドが自身を呼んでも、あくまでメソッドロジックの再利用をしているだけで、メソッドの中で宣言しているローカル変数は再利用していないのです。ローカル変数は、そのメソッドを呼び出すたびに、別の領域に生成し直され、呼び出し元と呼び出し先では名前が一致しても別物の変数として扱われます。 そして引数である depth も、メソッド内だけで有効なローカル変数なのです。 1回目のrecursionMethodメソッド内のdepth(※1)は0で始まり1になりました。 その後、recursionMethodメソッドを呼び出しています。その引数であるdepth(※2)は、※1のdepthと別に生成されて、※2のdepthの値をいくら変更しようと※1のdepthは一切影響を受けません。 流れを図示すると以下の通りになると思います。 mainメソッド ┌ │→呼出 │ recursionMethodメソッド │ ┌ │ │このメソッド内のdepthは、0で初期化 │ │*とdepth(0)を出力 │ │depth++ │ │このメソッド内のdepthは、1に変化。 │ │→呼出 │ │ recursionMethodメソッド │ │ ┌ │ │ │このメソッド内のdepthは、1で初期化 │ │ │*とdepth(1)を出力 │ │ │depth++ │ │ │このメソッド内のdepthは、2に変化。 │ │ │→呼出 │ │ │ recursionMethodメソッド │ │ │ ┌ │ │ │ │このメソッド内のdepthは、2で初期化 │ │ │ │*とdepth(2)を出力 │ │ │ │depth++ │ │ │ │このメソッド内のdepthは、3に変化。 │ │ │ │return │ │ │ └ │ │ │←戻り │ │ |このメソッド内のdepthは、2のまま │ │ │#とdepth(2)を出力 │ │ └ │ │←戻り │ |このメソッド内のdepthは、1のまま │ │#とdepth(1)を出力 │ └ │←戻り └

daisy_k
質問者

お礼

run34rickyさん、ご回答ありがとうございます。 非常に分かりやすいご説明で、フローがしっかり 理解できました。 大変勉強になりました。 ありがとうございました。

その他の回答 (4)

  • betagamma
  • ベストアンサー率34% (195/558)
回答No.5

No.1です。run34rickyさんの図を見ればわかるのではないでしょうか? 実は,自分もこのように、字下げして書いたのですが,投稿したら字下げが消されてしまって・・・字下げが消されたら意味不明ですよね.申し訳ありません。

  • ngsvx
  • ベストアンサー率49% (157/315)
回答No.3

再帰呼び出しは、ややこしくなることがよくありますね。 とりあえず、入り口(1箇所)と出口(2箇所)で表示したらどうですか? その時に、どの深度なのかを表示したところですが、 このソースでは引数depthを直接操作しているので、 入り口の直後で int curDepth = depth; などとして、入り口と出口でcurDepthを表示したらどうでしょうか。

daisy_k
質問者

お礼

ご回答ありがとうございました。

回答No.2

こんにちは。 > だとしたら、なぜ depth がデクリメントされるのですか? デクリメントされているわけではありません。 元々の呼び出し元に戻っているだけです。 main →recursionMethod(A) depth=0→depth++→depth=1 →recursionMethod(B) depth=1→depth++→depth=2 →recursionMethod(C) depth=2 →recursionMethod(B) depth=2 →recursionMethod(A) depth=1 →main

daisy_k
質問者

お礼

ご回答ありがとうございました。 処理の流れを理解する参考になりました。

  • betagamma
  • ベストアンサー率34% (195/558)
回答No.1

まず、returnで直接mainに戻るわけではありません。return戻るのは,呼び出し先の関数です。ここがおそらく理解していないポイントなのではないかと思います. このrecursionMethod(recursiveMethodの方が英語としては正しいですが)は、 A:***depthを表示する B:val++;if(depth++>=MAX_DEPTH)return; C:recursionMethod(depth) D:####depthを表示する の四箇所から構成されています。 A:depth=0 B:depth=1,val=1 C:depth=1でrecursionMethodを呼び出す。 A:depth=1なので二行目を表示(val=2) B:depth=2,val=2 C:depth=2でrecursionMethodを呼び出す. A:depth=2なので三行目を表示(val=2) B:val=3にしてから、returnで戻る D:☆depth=2,val=3で、四行目を表示。 D:depth=1,val=3で、5行目を表示 わかりましたでしょうか?ここで、気をつけていただきたいのは、depthとvalの根本的な違いです。☆のところがおそらくわかっていらっしゃらないのだと思うのですが、 void func(int depth){ depth++; A(depth); System.out.println(depth); } という関数があったとしたら、Aがどのような関数であったとしても、受け取ったdepth+1の値がそのまま表示されるはずですよね?今は、このAがrecursionMethodになっているだけです。 一方、valは引数ではないので、実体は一つです。

daisy_k
質問者

補足

betagammaさん、早速のご回答ありがとうございます。 いくつか確認させて頂きたいのですが、 よろしくお願い致します。 > A:depth=1なので二行目を表示(val=2) この部分では まだ val=1 だと思うのですが、どうでしょうか? > B:val=3にしてから、returnで戻る ここの returnで戻る戻り先は、recursionMethod(depth); ということなのですね。 recursionMethod(0); の呼び元である main()に返るのだと思っていました。 そうすると、4行目が出力されるのは納得できました。 しかし、最後の 1行(5行目)が出力されるのが やはり分かりません。 4行目を実行した時点で、recursionMethod(depth) の処理は 終了し、main()に返るような気がするのですが… eclipse のデバッグモードで 1行1行 実行していくと、4行目を出力後、カーソルは } // recursionMethod() の終了括弧 に移動して再び System.out.println("###~ を実行し、5行目を出力後 main()に返ります。 これも戻り先が main()ではなく、 recursionMethod(depth) だからということなのですよね? だとしたら、なぜ depth がデクリメントされるのですか? 長々とまとまりのない説明で申し訳ありませんが、 よろしかったら補足説明を頂けますでしょうか。

関連するQ&A

  • getter、setterメソッドを活用したいです

    Javaのgetter、setterメソッドをうまく使って、下記のコードを実行しようとしたのですが。 ---------------- package javatest; public class Jidosha { private int num; private double gas; public void setNum(int num) { this.num = num; } public void setGas(double gas) { this.gas = gas; } public int getNum() { return num; } public double getGas() { return gas; } public static void main(String args[]) { System.out.println("この車のナンバーは" + num + "です。"); System.out.println("この車のガソリンが入る最大量は " + gas + "です。"); } } ---------------- Eclipse上では「System.out.println・・・」のところで「非 static フィールド gas を static 参照できません」といったエラーが出てきまして。 getter、setterメソッドをうまく活用して「この車のナンバーは××××です」「この車のガソリンが入る最大量は〇〇〇〇です。」という文を出力するには、どう書けばよいでしょうか。 よろしくお願い致します。 ※OKWAVEより補足:「Webシステム開発」についての質問です。

    • ベストアンサー
    • Java
  • プログラムの合体。

    jump7799_4836さん 2018/7/2103:34:44 public class Test2 { public static int メソッド () { System.out.println("メソッド()が呼ばれました"); return 0; } public static int メソッド (int i) { System.out.println("メソッド(int i)が呼ばれました"); return i; } public static int メソッド (int i, int j) { System.out.println("メソッド(int i, int j)が呼ばれました"); return i; } public static int メソッド (short s) { System.out.println("メソッド(short s)が呼ばれました"); return s; } public static int メソッド (double d) { System.out.println("メソッド(double d)が呼ばれました"); return (int) d; } } public メソッド class Test2 { public メソッド static void main(String[] args) { Test2.メソッド (); Test2.メソッド (1); Test2.メソッド (1,2); Test2.メソッド (1.1); Test2.メソッド ((short) 1); Test2.メソッド ((double)1); } } kouzou@GD348ZZD9:/WORK$ javac Test2.java Test2.java:29: エラー: class、interfaceまたはenumがありません public メソッド class Test2 { ^ Test2.java:30: エラー: <identifier>がありません public メソッド static void main(String[] args) { ^ エラー2個 2つのプログラムが合体することってあるのですか? いつもすみません。 解説のほど、宜しくお願い申し上げます。

    • ベストアンサー
    • Java
  • メソッドの再帰呼び出しについて

    再帰処理に関する質問をさせていただきます。私は今複数のarraylistの中に入っているオブジェクトを順列を用いてすべての並びを所得するプログラムを作っています。プログラムが長く汚いので(汗)、例で示させていただきます。 (例) ArrayList t1; ArrayList t2; t1の中身[obj1,obj2] t2の中身[obj3,obj4] 結果:以下のように4つオブジェクトを作成 [obj1,obj3],[obj1,obj4],[obj2,obj3],[obj2,obj4] それに伴い、メソッドを作り再帰呼び出しを行っているんですが、returnでメインに返りません。戻り値はvoidです。また、 return; System.out.println("check"); と書くと、checkが出力されてしまっています(しかも何個も)。IndexOutOfBoundsExceptionのエラーがでるのですが、先にreturnを処理するはずなのになぜだろうと悩んでいます。 return以外にメインに戻る方法はあるんでしょうか?またreturnでメインに戻らない理由を知っている方がいるなら、ぜひ教えていただきたいです。よろしくお願いします。

  • 木構造と再帰

    1週間考えましたが分かりません 正の整数mとn(n <= 9)を入力して、1以上n以下のn個の数字を自由に組み合わせて構成されるm桁の数字列のすべてを表示するJavaのプログラムである。 以下に、本プログラムの動作例を示す。 桁数を入力してください 4(入力) 1以上9以下の整数を入力してください 2(入力) 1111 1112 1121 1122 1211 1212 1221 1222 2111 2112 2121 2122 2211 2212 2221 2222 このとき、本プログラムのsetNode()メソッドの中身を記述してプログラムを完成させよ。ただし、setNodeメソッドは再帰的手続きとし、プログラムのほかの部分を変更(追加、削除を含めて)しないこと。 import java.io.*; class Node { private int value; private Node child[]; public void setNode(int value, int digit, int range); { //ここに解答 } public int getValue() { return value; } public Node getChild(int index) { return child[index]; } } class NumberString { static int range; public static void main(String args[]) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.plintln("桁数を入力してください"); String str1 = br.readLine(); int digit = Integer.parseInt(str1); System.out.plintln("1以上9以下の整数を入力してください"); String str2 = br.readLine(); range = Integer.parseInt(str2); Node root = new Node(); root.setNode(0, digit, range); for(int i = 0;i < range; i++){ printNumber(root.getChild(i), digit, ""); } } private static void printNumber(Node n, int digit, String str) { if(digit == 1){ System.out.plintln(str + n.getValue()); return; } for(int i = 0;i < range;i++){ printNumber(n.getChild(i), digit - 1, str + n.getValue()); } } }

    • ベストアンサー
    • Java
  • 敬称について教えてください

    class parent{ protected final static int val=10; } class Child extends Parent{ private int val=100; public void method(String s){ System.out.println(++val)} } } class Test{ int val=10; public static void main(String args[]){ Parent c=new Child(); c.method("HELLO"); } } 初歩的な質問ですみません。教えて下さい。 上記プログラミングで、下から3行目のc.method・・・ を記入してもコンパイルエラーがおこらないのはなぜでしょうか? 今回newしているのはChildのオブジェクトです。 が、型はParentです。 Parentにはmethodはないし、 なぜこれが許されるのかがわかりません。 分かりやすくおしえてもらえませんか?

    • ベストアンサー
    • Java
  • メソッドの呼び出し方

    サブクラスを記述する下記のプログラムについて質問します。 class Car { //タイヤ private String tire = ""; public Car() { tire = "タイヤ"; } /* *どんなタイヤであるかを設定 *value タイヤの種類 */ public void setTire(String value) { tire = value + "のタイヤ"; } /* *作成されたタイヤを返します *return タイヤ */ public String getTire() { return tire; } } class Motorcar extends Car { private String tire = ""; private String body = ""; private String handle = ""; String type = ""; public Motorcar() { tire = "タイヤ"; body = "車体"; handle = "ハンドル"; } public String getTire() { return tire; } public String getBody() { return body; } public String getHandle() { return handle; } public String createMotorcar(String t) { type = t; tire = type + "のタイヤ"; return tire; } } public class Sample1 { public static void main(String atgs[]) { Motorcar car = new Motorcar(); String type = "トラック"; String tire = car.getTire(); String body = car.getBody(); String handle = car.getHandle(); System.out.println("タイヤ=" + tire); System.out.println("車体=" + body); System.out.println("ハンドル=" + handle); //どんな車かを設定 car.createMotorcar(type); tire = car.getTire(); body = car.getBody(); handle = car.getHandle(); System.out.println(type + "の作成!!"); System.out.println("タイヤ=" + tire); System.out.println("車体=" + body); System.out.println("ハンドル=" + handle); } } これを実行したら、 タイヤ = タイヤ 車体 = 車体 ハンドル = ハンドル トラックの作成!! タイヤ = トラックのタイヤ 車体 = 車体 ハンドル = ハンドル となりました。 後半の車体・ハンドルにも「トラックの」という言葉 を入れるには、どういうふうにメソッドを記述したらよいのでしょうか? ちなみに問題では、サブクラスだけ自分で記述し、スーパークラス・ 実行クラスはこのまま利用することとします。

    • ベストアンサー
    • Java
  • クラス変数/メソッドとインスタンス変数/メソッドの見え方について

    Javaの言語仕様(?)についてお教え下さい。 参照変数の型がインスタンスのスーパークラスの型の時、クラス変数/メソッドとインスタンス変数/メソッドの見え方が納得いかずに困っています。 以下のような条件の時です。   ・クラスが2つあり、1つはもう1つのクラスを継承しています。     それぞれを「スーパー」「サブ」と以下で呼びます。   ・インスタンスは"サブ"の型です。   ・上記インスタンスへの参照変数は"スーパー"のクラスの型です。   ・"スーパー"、"サブ"ともに【同名】の「クラス」変数/メソッド、「インスタンス」変数/メソッドがあります。 この場合に、"サブ"のインスタンスを参照している"スーパー"の型の変数を介し、それらにアクセスしたらどれが見えるか?という疑問です。 実験結果では以下のようになりました。           [フィールド]  [メソッド]   [1.static ]  スーパーの   スーパーの   [2.非static]  スーパーの   サブの 納得いかないのは「2.非static」で「フィールド」が、「スーパーの」になるところです。 これも「サブの」になると思っていました。 なぜサブクラスのが見えないのでしょうか? 同名の変数なのでスーパークラスのはサブクラスので隠れされ、サブクラスのが見えると思っていたのですが。 参考書には以下のように書いてありました。   フィールドの場合、参照変数の型のものが見える。   メソッドの場合、インスタンスの型のものが見える。 私には不自然に感じられるのですが、「こういう仕様だから。」と納得するしか無いのでしょうか? 「なぜこうなるか」について説明がある文献、サイトなどありましたらお教えください。 参考までに以下が実験したサンプルコードと結果です。長くて申し訳ありません。 「サンプルコード」 public class Super { static int staticField = 10; int instanceField = 100; static void staticMethod() { System.out.println( "staticField = " + staticField ); } void instanceMethod() { System.out.println( "instanceField = " + instanceField ); } } public class Sub extends Super { static int staticField = 90; int instanceField = 900; static void staticMethod() { System.out.println( "staticField = " + staticField ); } void instanceMethod() { System.out.println( "instanceField = " + instanceField ); } } public class TestStatic { public static void main(String[] args) { // インスタンスはSub、参照変数もSub、という状態。 Sub sub = new Sub(); System.out.println( "実験1" ); System.out.println( "1.クラス変数      " + sub.staticField ); System.out.print( "2.クラスメソッド    " ); sub.staticMethod(); System.out.println( "3.インスタンス変数   " + sub.instanceField ); System.out.print( "4.インスタンスメソッド " ); sub.instanceMethod(); // インスタンスはSub、参照変数はSuper、という状態。 Super sup = new Sub(); System.out.println( "実験2" ); System.out.println( "5.クラス変数      " + sup.staticField ); System.out.print( "6.クラスメソッド    " ); sup.staticMethod(); System.out.println( "7.インスタンス変数   " + sup.instanceField ); System.out.print( "8.インスタンスメソッド " ); sup.instanceMethod(); } } 「結果」 実験1 1.クラス変数      90 2.クラスメソッド    staticField = 90 3.インスタンス変数   900 4.インスタンスメソッド instanceField = 900 実験2 5.クラス変数      10 6.クラスメソッド    staticField = 10 7.インスタンス変数   100 8.インスタンスメソッド instanceField = 900 納得が行かないのは7のところです。 以上よろしくお願いいたします。

    • ベストアンサー
    • Java
  • クラスメソッドについて

    Kakeibo クラスにあるsyojikin(初期値0)をprivate フィールドとし、その代わりにクラスメソッドshowSyoji(戻り値、引数無し)を用意して、下記のプログラムを作成したのですがクラスメソッドのshowSyojiがうまく回りません。これはもしかしてクラス変数も必要なのですか(privateもつけて)?また、このプログラムはどのようなプログラムになるのですか? class Kakeibo { private int year; private int month; private int date; private int syunyu; private int shisyutsu; private int syojikin; private Kakeibo() { year = 0; month = 0; date = 0; syunyu = 0; shisyutsu = 0; syojikin = 0; } public Kakeibo(int y, int m, int d, int sy, int sh) { this(); if(sy>=0 && sh>=0){ year = y; month = m; date = d; syunyu = sy; shisyutsu = sh; } else{ System.out.println("入力した金額は無効です。"); } } public static void showSyoji() { System.out.println("所持金:"+syojikin+"円"); } public void showData() { System.out.print(year+"年"+month+"月"+date+"日:収入"+syunyu+"円、支出"+shisyutsu+"円\n"); } } class KNO5 { public static void main(String args[]) { Kakeibo.showSyoji(); //Kakeibo kakeibo1 = new Kakeibo(); //kakeibo1.showData(); Kakeibo kakeibo2 = new Kakeibo(2010, 12, 1, 3000, 500); kakeibo2.showData(); Kakeibo kakeibo3 = new Kakeibo(2010, 12, 1, -5000, 300); kakeibo3.showData(); Kakeibo kakeibo4 = new Kakeibo(2010, 11, 7, 0, 1000); kakeibo4.showData(); Kakeibo kakeibo5 = new Kakeibo(2010, 11, 18, 100, 800); kakeibo5.showData(); Kakeibo.showSyoji(); } }

    • ベストアンサー
    • Java
  • 初歩的なことなのですが(メソッドについて)

    こんにちわ。 最近、javaの勉強を始めました。 今、掛け算を求めるメソッドと、そのメソッド結果を表示するためのメソッドをつくっています。 掛け算を求める方は class Rensyu { public int Kakezan (int atai1,int atai2,int atai3) { int seki = atai1 * atai2 * atai3; return seki;          } と作ったのですが、 この後ろに      public void Show(){ System.out.println(); } という形でKakezanメソッドの値を出力するメソッドを加えたいのですが、書き方がわかりません。説明がわかりづらいと思いますが、よろしければ、回答(ソースの書き方)をお願いします。

    • ベストアンサー
    • Java
  • 文が指定されていませんエラーについて

    public class Stack2 { private static int[] stack = new int[50]; private static int ip = -1; //スタックポインター, -1ならデーターなし public static int push ( int inData ) { // データー格納 if (ip == (stack.length - 1)) { System.out.println(\"スタックがいっぱいです\"); } else { stack[++ip] = inData; } } public static int pop ( ) { // データー取り出し if ( ip < 0 ) { System.out.println (\"スタックにデーターがありません\"); return -2147483648; } else { return stack[ip]; } } } 「Stack2.java:10:return 文が指定されていません」コンバイルエラーになります。 どのようにデバックすれば良いのか教えてください。

専門家に質問してみよう