• ベストアンサー

PythonVer3の再帰について

現在、再帰について勉強しています。 関数のステートメントブロックの中に同じ関数を2か所使った場合の、処理される順番がよく解りません。 そこで、プログラムが表示されている状態で、Enterキーを押す毎に1行ずつ処理が進んで行き、今どの行が働いたかが分る様なソフト又は方法がないでしょうか。 よろしくお願いいたします。

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

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.4

実行順序は、あくまで、次の通りです。 02) 03)→ifを満していたら、04)のreturnで終了 05) 06) 07) 08) 09) 10)で関数の最後に来たので(自動的にreturnで)終了 再帰の先まで含めた動作順序を考えるときも、他の関数と考え方は一緒です。 ・returnしたら、呼び出したところへ戻ってきます。 ・ローカル変数は、現在実行中の関数の中だけで有効です。 #2で述べたように、別の関数だと思うくらいがいいです。 02) ここのlengを leng_0とする 03)→ifを満していたら、04)のreturnで終了 05) 06)→br(leng-1)の呼び出し   1-02) ここでのlengをleng_1 とする。 leng_1とleng_0とは別の変数になる   1-03)→ifを満していたら、1-04)のreturnで終了   1-05)   1-06)→br(leng-1)の呼び出し     1-1-02) ここでのlengをleng_1_1 とする。 leng_1_1とleng_1とleng_0とは別の変数になる     1-1-03)→ifを満していたら、1-1-04)のreturnで終了     1-1-05)     1-1-06)→br(leng-1)の呼び出し       ...     ←戻ってくる     1-1-07)     1-1-08)→br(leng-1)の呼び出し       ...     ←戻ってくる     1-1-09) ここでのlengはleng_1_1     1-1-10)で関数の最後に来たので(自動的にreturnで)終了   ←戻ってくる   1-07)   1-08)→br(leng-1)の呼び出し     ...   ←戻ってくる   1-09) ここでのlengはleng_1   1-10)で関数の最後に来たので(自動的にreturnで)終了 ←戻ってくる 07) 08)→br(leng-1)の呼び出し   2-02) ここでのlengをleng_2 とする。leng_2は、 leng_1ともleng_0とも別の変数になる   2-03)→ifを満していたら、2-04)のreturnで終了   2-05)   2-06)→br(leng-1)の呼び出し     ...   ←戻ってくる   2-07)   2-08)→br(leng-1)の呼び出し     ...   ←戻ってくる   2-09) ここでのlengはleng_2   2-10)で関数の最後に来たので(自動的にreturnで)終了 ←戻ってくる 09) ここでのlengはleng_0 10)で関数の最後に来たので(自動的にreturnで)終了 > 06)でleng-1が4になって ということは、leng=5ですね(※1) > 02)に飛び ステップ実行では同じ02)に飛んだように見えますが、実際には 1-02)に該当するものに飛んでいます。 > 04)のreturnで これも、04)ではなく 1-04)です > 07)に飛び「b」をprintして returnとあるように、「飛んで」きたのではなく「戻って」きます。さらに言えば、06)の後に戻ります。 「飛ぶ」だと、どこへでも行けるような感じですが、「戻る」だと「呼んだところへ戻る」というニュアンスがあって、動作を的確に表現していると思います。 > 08)の関数2において、lengが「6」になり (※1)にあるように、leng=5です。この関数中で、lengが変更されるものはありません。 なので、「lengが6になる」ことはありません。 「lengが5になる」こともありません 「lengは最初から5のまま」です。 同じ行を実行しているのに、違う関数になっている、という点が再帰の理解しづらい点ではないか、と思います。

kakekomi
質問者

お礼

Kmeeさま 大変申し訳ありませんが、私のスキルではどうしても理解できません。 Pythonの参考書を最初から読み直してみます。 しばらくしてから、又今回と関連した質問をすると思いますので、その時にもう一度回答お願いいたします。

その他の回答 (3)

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.3

「5」でないなら、何が来ると思いますか? なぜそう考えたのか、と、実際の動作の違いを知るのが、理解への近道な気がします。 その表示は、それぞれ、次のように実行されたでのものです。 a※ 元の関数 br(7)の 05)  ※ 元の関数 br(7)→関数1 br(7-1)の実行 a  ※ 元の関数 br(7)→関数1 br(7-1)の 05)    ※ 元の関数 br(7)→関数1 br(7-1)→関数1 br(6-1)の実行 a    ※ 元の関数 br(7)→関数1 br(7-1)→関数1 br(6-1)の 05)      ※ 元の関数 br(7)→関数1 br(7-1)→関数1 br(6-1)→関数1 br(5-1) → leng=5-1=4なので 04)でreturn b    ※ 元の関数 br(7)→関数1 br(7-1)→関数1 br(6-1)の 07)      ※ 元の関数 br(7)→関数1 br(7-1)→関数1 br(6-1)→関数2 br(5-1) → leng=5-1=4なので 04)でreturn 5    ※ 元の関数 br(7)→関数1 br(7-1)→関数1 br(6-1)の 09), leng = 6-1=5 c    ※ 元の関数 br(7)→関数1 br(7-1)→関数1 br(6-1)の 10)    ※ 元の関数 br(7)→関数1 br(7-1)→関数1 br(6-1) の終了 b  ※ 元の関数 br(7)→関数1 br(7-1)の 07), leng=6

kakekomi
質問者

補足

何度もありがとうございます。 ”5”の後には何が来るかですが 06)でleng-1が4になって 02)に飛び 03)でifが働いて 04)のreturnで 07)に飛び「b」をprintして 08)の関数2において、lengが「6」になり 02)に飛んで と解釈して、後は解からなくなりました。 処理の順番を01)、02)を使って、表示出来ないでしょうか。 なにせ初心者ですので、何か基本的な知識が欠けているのかも知れず、非常に恐縮しています。 急ぎませんので、すぐに返事をいただかなくても結構です。 よろしくお願いいたします。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.2

再帰していないところを省略して。 def br(leng):       #元の関数   if leng < 10:     return   br_1(leng/2)      #関数 1番   br_2(leng/2)      #関数 2番 def br_1(leng):       #関数1   if leng < 10:     return   br_1_1(leng/2)      #関数1-1番   br_1_2(leng/2)      #関数1-2番 (以下同様に、沢山のbr_*_*..) このプログラムなら、どういう順番で動作するか、わかりますね? 再帰呼び出しは、上記のような「同じ内容の関数が沢山あって、それを順番に呼び出す」ようなものです。 こう考えれば、疑問が解けるのではないでしょうか。 > 1)関数1番、2番の(leng/2)は最初はそれぞれ(200/2)が代入されるんでしょうか。それとも関数2番には最初から(12.5/2)が代入されるんでしょうか。 br_2(leng/2)がどうなるか考えれば、わかると思います。 brのlengとbr_1のlengは別なものなので、br_1のlengが変化しても、brには影響ありません。 # グローバル変数とか、def ts(a): a.set(1) 等と、呼び出し元のオブジェクトを変更するような仕組みを使っていれば、呼び出し元でも変化します。 # が、これは再帰とは関係の無いことです。 あと、br(200)と整数型で呼び出していますので、lengには常に整数型が入ります。(整数型/整数型は整数型になります) 「関数2番には最初から(12.5/2)」という状態になっていたとしても、12.5にはなりません。 > 2)「関数2番」から「元の関数」に行き、returnが働いた場合、「関数1番」の次の行に行くんでしょうか。それとも「関数2番」の次の行に行くんでしょうか。 「関数2番」から行くのは「関数2番から呼び出された(別の)『元の関数』」であって、「(現在実行中の)元の関数」ではありません。 上記の例で言えば、 brからbr_2を呼び出し、br_2でreturnで戻ったら、brのどこに戻るか、ということです。 ステップ実行だと、同じ関数の中で行ったり来たりしているように見えて、かえって混乱するかもしれません。 「現在実行中の元の関数」の関数1 ↓ 「「現在実行中の元の関数」の関数1として呼び出された『元の関数』」の関数1  ↓ 「「「現在実行中の元の関数」の関数1として呼び出された『元の関数』」の関数1」として呼び出された『元の関数』   ↓ ...  ↓ 「「現在実行中の元の関数」の関数1として呼び出された『元の関数』」の関数2 ↓ 「現在実行中の元の関数」の関数1の次の行 .... となるので、実行箇所だけ見ていたら 関数2でreturn→関数1の次の行 と実行しているように見えてしまうかもしれません。

kakekomi
質問者

補足

kmeeさま 何度も回答ありがとうございます。 せっかくですが、よく理解できません。 自分の解らない所が質問しやすい様にプログラムを作りましたので、これで説明させていただきます。 01)from turtle import* 02)def br(leng):      #元の関数 03)  if leng<5: 04)    return 05)  print("a") 06)  br(leng-1)     #関数1 07)  print("b") 08)  br(leng-1)     #関数2 09)  print(leng) 10)  print("c") 11)br(7) 12)print("d") 13)input() これを実行しますと a a a b 5 c b * * * と表示されます。 a a a b までは理解出来るんですが、次がどうして5なのかが解りません。 この部分が理解出来たら、全て解るんではないかと期待しています。 解説お願いいたします。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.1

http://docs.python.jp/3.3/library/pdb.html とか。 こんなプログラムのことでしょうか? 1:def func(a): 2: if 終了条件 : return 3: func(b) #ここと、 4: func(c) #ここで再帰呼び出し 再帰呼び出しは、呼んだ先の順番まで考えようとすると、ややこしくなります。 呼んだ先での動作は考えず、現在の動作だけを考えます。これだと、実行順序は 1→2(終了条件を満していたら、ここで終了)→3→4 になりますよね?func関数内の実行順序は以上です。 例えば print("X") としたときに、printの中の実行順序まで考えてますか?

kakekomi
質問者

お礼

kmeeさま 整理した質問をもう一度質問させていただきます。 本「みんなのPython」のなかの例題です。 from turtle import * def br(leng):       #元の関数  """長さを引数として受取って枝を描く """   if leng < 10:     return   forward(leng)   left(30)   br(leng/2)      #関数 1番   right(60)   br(leng/2)      #関数 2番   left(30)   forward(-leng) br(200) input() このプログラムの動きを1行ずつ確認したんですが、どうしても理解出来なかったので、何か良い確認方法がないか、質問いたしました。 あらためて質問させていただきます。 1)関数1番、2番の(leng/2)は最初はそれぞれ(200/2)が代入されるんでしょうか。それとも関数2番には最初から(12.5/2)が代入されるんでしょうか。 2)「関数2番」から「元の関数」に行き、returnが働いた場合、「関数1番」の次の行に行くんでしょうか。それとも「関数2番」の次の行に行くんでしょうか。 表現がややこしいかも分かりませんが、よろしくお願いいたします。

kakekomi
質問者

補足

kmeeさま 回答ありがとうございます。 とりあえず、お礼だけを言って回答の内容を整理してからもう一度質問させていただきます。

関連するQ&A

  • 再帰呼びだし

    再帰呼びだし 問題1 再帰呼び出しを用いてint型の配列({-9、8、-7、6、5、4、1、3、6、9、2、-14})の最小値を求めて出力するプログラムを作成せよ。関数名はmin_of_arrayとする。 問題2 同じく再帰呼び出しを用いてint型の配列({3、4、1、5、2})を小さい方から順番に求めて出力するプログラムを作成せよ。 再帰呼び出しが苦手で、じっくり解いていこうと思ったのですが、他の課題もあって、もう提出期限いっぱいいっぱいなので載せました。 どうかよろしくお願いします。

  • 再帰的処理について

    指定したURLのページのHTMLを取得しその中のリンクを再帰的に表示するプログラムを考えていますが再帰的な処理の部分がよく理解できません。教えて下さい。お願いします。

    • ベストアンサー
    • Perl
  • 再帰的な関数の作り方

    C言語の勉強をしている初心者です。 まだまだ始めたばかりでよく分かっていない状態です。 再帰的な関数が便利そうな事が書かれているのをよく目にしますが、何が便利でどう作ればいいのか分かりません。 分かりやすく教えていただけませんか。

  • 出口(戻り値) 再帰処理

    情報処理を独学で勉強しています。4月の初級シスアドに合格して,元々初級シスアドの午前を基本情報技術者の参考書で勉強していて,基本情報技術者の午前が70%ぐらいとれるようになってきたので,午後の勉強を「基本情報技術者 午後 完全合格教本」という本を買って始めました。 タイトルの「出口(戻り値)」この関数を呼び出したプログラムに戻り値を返すというのが再帰処理(再帰を使わないn!プログラム)の中でどうゆう処理をしているかが分かりません。 ○ プログラム名:再帰なしnの階乗 ○ 整数型 : 答 ・答 ← Fa(5) ・表示命令(答)     {答の内容を表示する} ○関数名: Fa(n) ○整数型: ANS ▲ n>1 | Yes |    ・Ans←n*Fb(n-1) ◇ No   |    ・Ans←1 ▼ ・出口(Ans)   (1) これがFa(n)がFb(n),Fc(n),Fd(n),FbがFcFdFe同じのが3回続きます。その後に ○関数名: Fe(n) ○整数型: ANS ▲ n=1 | Yes |    ・Ans←1 ▼ ・出口(Ans)   (2) となります。 参考書の実行の様子というのを見ると、最初は出口(Ans)をずっととばして最後の(2)のところまで来て戻るみたいになっているんですが、何がどこに戻ってどうなっているか、私の知っている知識の中で説明ができる方、よろしくお願いします。

  • 再帰的(リカーシブ)プログラムの説明について。

    以下は、再帰的(リカーシブ)プログラムの説明を記載しました。 この説明文でおかしい箇所の添削をお願い出来ないでしょうか? 宜しくお願い致します。 以下からになります。 再帰的(リカーシブ)プログラムとは、プログラムの中から自分自身を呼び出して実行することを再帰的(リカーシブ)アルゴリズムといい、この形式で再帰呼び出しを行うプログラムのこと。 まずは、再帰的アルゴリズムについて、例を使って説明を行いたい。 主プログラムとサブルーチンaがある。 主プログラムは、文字通り、主(メイン)となるプログラム。 サブルーチンは、主プログラムが呼び出して利用する処理をひとまとめにしたもの。 文字通り、サブとなる処理を行う。 主プログラムには、CALL aという命令が記述されている。 これはサブルーチンaを読み出すという命令。 この再帰的プログラムは、処理が終わったら、読み出された場所に帰っていく。 このため、戻り場所を記憶しておかないと帰る事が出来ない。 この戻り場所を記憶するのが、LIFO方式による記憶領域になる。 LIFO方式の記憶領域だから、スタック領域になる。 スタック領域だから、後入れ先出しで戻り場所を記憶していく。 まずは、1回目の呼び出しとして、主プログラムがサブルーチンaを呼び出している。 1回目の戻り場所を記憶しておく。 次にサブルーチンaを見ると、CALL a、つまり自分自身を読み出している。 これが2回目の読み出し。 このように自分自身を呼び出すことを再帰呼び出しという。 同じプログラムの中で自分自身を読み出しているのだが、コンピューターは、あたかも別のサブルーチンがあるように処理が行われている。 この場合、それぞれの処理で、別の変数を用意しながら処理を行う。 このサブルーチンで処理が終わった場合にも、もとに戻る必要がある。 これは2回目の呼び出しになるため、2回目の戻り場所を記憶しておく。 更に、3回目として再びサブルーチンaを呼び出す。 3回目の戻り場所を記憶し、また別の変数を用意しながら処理を行う。 ここで最後のサブルーチンで処理が終わったとする。 処理が終わったら、呼び出された場所に戻る。 戻り場所の記憶を見てみると、上から戻る順番に記録されていることがわかる。 戻り場所はLIFO方式、後入先出しで記録されているから、最後に呼び出した3回目の戻り場所が1番上に記録され、次に2、最後に1が記録されている。 最初は戻り場所を記憶した記憶領域を参照して、3回目に呼び出された場所に戻る。 ここで3の戻り場所が消える。 そして引き続き処理が行われる。 次に、2回目に呼び出した処理が終わり、2回目に呼び出された場所に戻り、2の戻り場所が消える。 また引き続き処理が行われ、1回目に呼び出した処理が終わり、1回目に呼び出された場所(主プログラム)に戻り、1の戻り場所が消える。 そして処理が行われ、プログラム全体が終了する。 このように、プログラムの中で自分自身を呼び出し、戻り場所を記憶しながら実行するようなプログラムを再帰的(リカーシブ)プログラムという。

  • 再帰呼び出しについて

    C言語のプログラムについてです。 テキストからランダムに並べられたアルファベットを順番にひっぱってきて、 二分木の構造にアルファベットをアルファベット順に格納していき、その順番で表示するプログラムなのですが、 typedef struct rec{ char word[1]; struct rec *left; struct rec *right; }rec; という構造体が定義されており、 二分木構造に(なるように)アルファベットを順番に格納していったあと、 それをアルファベット順に表示していく関数が以下の関数hyoujiなのですが、 (二分木を生成する際にmallocを使用しているので、freeがありますが今回は無視してください。) void hyouji(rec *top){ if(top!= NULL){ hyouji(top->left); printf("%s\n",top->word); hyouji(top->right); free(top); } } この再帰呼び出しの動きがよくわかりません。(つまりはhyoujiの関数の動きがわかりません。) *topは二分探索木の一番上のアドレスを示しています。 wordにアルファベットが格納されています。 たとえば、下のように格納されていたらどのようにhyouji関数は動くのでしょうか。 順番に説明していっていただけたらありがたいです。 (ちなみにプログラム的に下の例のように格納されていきます。 まずtopを生成して、テキストファイルの先頭のアルファベットを入れて、そこから順に 格納していくので、topがaとは限りません。) ........................[b] ..................../...\ .................[a]........[e] .........................../...\ .......................[c].........[g]

  • 再帰関数を用いて配列の合計を求める

    かなり(約三時間)悩んでまだ分からないので質問します。 再帰関数を用いて配列の中の数字(floatで定義されている)の合計を求めるプログラムを作っています。階乗を求めるプログラムの例を見ながらやっているのですがもう降参です。簡単だと思ったんですけど配列と組み合わせでもう頭がパニックです。どなたか答え(またはヒント)を教えてください。 よろしくお願いします。 なお、下のプログラムは私が1から作りましたので完全に間違っている可能性大です。参考にしないでください。(^^ゞ #include <iostream> using namespace std; float recur(int numF); int main() { int num; float sum; cout << "Enter an integer number: "; cin >> num; //再帰関数なんてこうやれば使わなくても出来るのに~! // for(i=0; i<num; i++) // sum += array[i]; sum = recur(num); cout << "The sum of all the numbers: " << sum << endl; return 0; } float recur(int numF) { int i; float array[numF]; //定数式が必要です、と怒られる for(i=0; i<numF; i++) return array[i] += array[i-1]; //ここに再帰の式が必要 }

  • c言語の再帰について教えてください。

    c言語を勉強してるんですが、再帰のイメージがなかなかつかめません。 例えば、入力した文字列を逆から一文字ずつ表示させるのには どうしたらいいのか教えてください! 入力:ABCDE E D C B A ソースで処理される順序など説明等していただけたらうれしいです。

  • C言語における再帰呼び出しの限界?について

    お世話になります、AEと申します。 次のような件に悩まされています。 ○画像のラベリング処理において、再帰呼び出しによって塗りつぶし処理を行っているのですが、再帰の回数が多くなると途中でメモリリークによるものと思われるエラーが発生し処理が中断してしまいます。 #ただし、物理メモリを全部使い果たした様子はありません。 ソースコードやエラーメッセージを添付できず、漠然とした質問で大変心苦しいのですが、一般論として、 ○Windows上で開発したプログラムにおいては、再帰の回数(あるいは再帰呼び出しのために確保されるメモリ量)は有限なのでしょうか?また有限であったとしてそれを拡張する設定があるのでしょうか? ということについてご意見などいただければと思います。 **** 無論、プログラム自体の不具合によってメモリリークを引き起こしているんじゃないの?とか、そもそもメモリが足りてないんじゃないの?というご意見もあるかと思いますが、それは取り敢えずおいておいて、一般的な意見として、「メモリの許す限り何回でもいけるぞ!」とか「同じような経験をしたぞ!」とかいう意見を伺えれば幸甚です。 合わせて解決策がもしあるものでしたらご意見ください。 一般的なUNIXではlimitでユーザが使えるメモリ量を設定できると思うのですが、そういう類の設定がWindowsにもあるぞ!というご意見などもお待ちしております。 勉強不足で申し訳なのですが、よろしくお願いいします。

  • VC++ 再帰関数

    ■キーボードより読み込んだ文字列の長さを求めるプログラムを再帰関数を使って作る #include <stdlib.h> int unk_r(char*p)   {    if(*p == 0x00)    return(0);   else    return(1+unk_r(p+1));   } int main()   {    char *i;    i = (char *)malloc(20);    scanf("%s",i);    printf("%d\n",unk_r(i));   } このプログラムについて、行、単語ひとつひとつ細かいところまで具体的に解説お願いします。どういう働きをしているのか等。 例えば #include <stdlib.h> はmallocを使うのに必要などなど。 よろしくお願いします。