質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

関数型プログラミング

関数型プログラミングとは、関数を用いて演算子を構築し、算出し、コンピュータプログラムを構成する枠組みです。

再帰

情報工学における再帰とは、プログラムのあるメソッドの処理上で自身のメソッドが再び呼び出されている処理の事をいいます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

1071閲覧

同一名関数を同時に再帰的に呼び出した場合の実行手順と実行結果の再利用のされ方について

sequelanonymous

総合スコア123

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

関数型プログラミング

関数型プログラミングとは、関数を用いて演算子を構築し、算出し、コンピュータプログラムを構成する枠組みです。

再帰

情報工学における再帰とは、プログラムのあるメソッドの処理上で自身のメソッドが再び呼び出されている処理の事をいいます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/09/20 08:25

同じ関数を再帰的に同時に二回呼び出した場合の実行順番があっているのかどうかが不安になります。
下記アウトプットに記載していますが、どういう順番で呼び出されていますでしょうか?
また、片方がbase caseでたどりついた場合、未実行の片方の関数の実行結果はどこにいってしまうのでしょうか?

下記、詳細な質問の説明と背景になります。
ーーー
例えば、下記コードでは、はじめに左側にある関数が再帰的にbase caseまで呼び出され、base caseまでたどり着いたあとは、右側の関数実行にいくのではなく、一回base caseで返したreturnの値を右側関数を無視してresに入力するって言う理解であっていますでしょうか?
res = recur_array_index(num, i-1) + recur_array_index(nums[1:], i)

私の最初の理解では、左側の関数呼び出しがbase caseまでたどり着いたら、下記のような形になり左側関数がbase caseにたどり着いたときの引数で右側の関数を実行すると思っていました。
res = [[]] + recur_array_index(nums[1:], i)

つまり、右側関数が同じくbase caseまで呼び出され、下記のような形でresが返ると思っていました。
res = [[]] + []

しかし、print文をつかって出力したところ、そういう順番でresが出力されていないことがわかり、どういう順番で実行されるのかなと思いました。

ーーー

python

1def recur_array(nums): 2 i = len(nums) 3 4 def recur_array_index(nums, i): 5 if i == 0: 6 return [[]] 7 elif not nums: 8 return [] 9 num = nums[1:] 10 print(f"nums: {nums}, num: {num}, i: {i}") 11 res = recur_array_index(num, i-1) + recur_array_index(nums[1:], i) 12 print(f"res: {res}") 13 return res 14 return recur_array_index(nums, i) 15 16if __name__ == "__main__": 17 nums = [1,2,3,4] 18 print(recur_array(nums))

アウトプット

shell

1$ python recursion.py 2nums: [1, 2, 3, 4], num: [2, 3, 4], i: 4 3nums: [2, 3, 4], num: [3, 4], i: 3 4nums: [3, 4], num: [4], i: 2 5nums: [4], num: [], i: 1 6res: [[]] ------------------> 左側の関数の返り値がresにはいって出力されている。 7nums: [4], num: [], i: 2 -----> 右側の関数が再帰的に呼び出されている??? 8res: [] --------------------> 右側の関数の返り値がresにはいって出力されている。 9res: [[]] ------------------> 左側の関数の返り値がresにはいって出力されている。なぜ、左側の関数を呼び出しはじめた??? 10nums: [3, 4], num: [4], i: 3 -----> 左側の関数が再帰的に呼び出されている??? 11nums: [4], num: [], i: 2 12res: [] --------------------> 右側の関数の返り値がresにはいって出力されている。 13nums: [4], num: [], i: 3 14res: [] 15res: [] 16res: [[]] 17nums: [2, 3, 4], num: [3, 4], i: 4 18nums: [3, 4], num: [4], i: 3 19nums: [4], num: [], i: 2 20res: [] 21nums: [4], num: [], i: 3 22res: [] 23res: [] 24nums: [3, 4], num: [4], i: 4 25nums: [4], num: [], i: 3 26res: [] 27nums: [4], num: [], i: 4 28res: [] 29res: [] 30res: [] 31res: [[]] 32[[]]

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

再起呼び出しを追いかけるには、ソースのprintの場所が不親切です。
最後の葉に辿りついたときに何も表示されません。

python

1def recur_array(nums): 2 i = len(nums) 3 4 def recur_array_index(nums, i): 5 print(f"nums: {nums}, i: {i}") 6 if i == 0: 7 return [[]] 8 elif not nums: 9 return [] 10 num = nums[1:] 11 12 res = recur_array_index(num, i-1) + recur_array_index(nums[1:], i) 13 print(f"res: {res}") 14 return res 15 return recur_array_index(nums, i)

このように、関数が呼ばれたところで、引数を表示するようにすれば、最後のとcろでもちゃんと表示されるようになります。
実行するここうなります。

関数の呼び出しの流れの図も書いてみました。(ざっと書いたので間違えているかも)

# の後は僕が書いたコメントで、番号は図中の呼び出しに対応しています。

text

1nums: [1, 2, 3, 4], i: 4 # 起動 2nums: [2, 3, 4], i: 3 # 1 3nums: [3, 4], i: 2 # 2 4nums: [4], i: 1 # 3 5nums: [], i: 0 # 4 上のif文でリターン 6nums: [], i: 1 # 5 下のif文でリターン 7res: [[]] # 4,5が両方返ったので、3のres出力 8nums: [4], i: 2 # 6 9nums: [], i: 1 # 7 下のif文でリターン 10nums: [], i: 2 # 8 下のif文でリターン 11res: [] # 7.8 が両方返ったので、6のres出力 12res: [[]] # 3,6 が両方返ったので、2のres出力 13nums: [3, 4], i: 3 # 9 14nums: [4], i: 2 # 以降はご自分でどうぞ。 15nums: [], i: 1 16nums: [], i: 2 17res: [] 18nums: [4], i: 3 19nums: [], i: 2 20nums: [], i: 3 21res: [] 22res: [] 23res: [[]] 24nums: [2, 3, 4], i: 4 25nums: [3, 4], i: 3 26nums: [4], i: 2 27nums: [], i: 1 28nums: [], i: 2 29res: [] 30nums: [4], i: 3 31nums: [], i: 2 32nums: [], i: 3 33res: [] 34res: [] 35nums: [3, 4], i: 4 36nums: [4], i: 3 37nums: [], i: 2 38nums: [], i: 3 39res: [] 40nums: [4], i: 4 41nums: [], i: 3 42nums: [], i: 4 43res: [] 44res: [] 45res: [] 46res: [[]] 47[[]] 48

イメージ説明

それにしてもこの関数、ただ巡回しているだけで何もしていないし、二又に別れているけど分割がおかしいのでそもそも巡回にも鳴っていない。まったく再帰の例としてはふさわしくありませんね。

投稿2021/09/20 09:58

TakaiY

総合スコア13790

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

同一名関数を同時に再帰的に呼び出した場合

同時に実行、ってのはありえません
単純に評価された順番で実行されます。また、その関数の実行が終わらないとその次へは行きません

投稿2021/09/20 08:32

y_waiwai

総合スコア88042

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sequelanonymous

2021/09/20 08:39

> 単純に評価された順番で実行されます。また、その関数の実行が終わらないとその次へは行きません コメントありがとうございます。 はい、私もそう思っていましたが、`print文`で出力したresの値をみるとそう実行されているようにみえるのですが、どうこのprint文結果を解釈すればいいでしょうか?
guest

0

ベストアンサー

一回base caseで返したreturnの値を右側関数を無視してresに入力するって言う理解であっていますでしょうか?

そんな動作はしません。左辺と右辺の値が揃わないと足し算は行われませんので、「足し算の結果をresへ代入する」という処理も行いようがありません。

私の最初の理解では、左側の関数呼び出しがbase caseまでたどり着いたら、下記のような形になり左側関数がbase caseにたどり着いたときの引数で右側の関数を実行すると思っていました。

オブジェクト自体を破壊的に変更してしまう場合は別ですが、再帰した時のローカル変数は実行ごとにバラバラに取られます

投稿2021/09/20 08:31

maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/09/20 08:38

> res: [[]] ------------------> 左側の関数の返り値がresにはいって出力されている。(いちばん上でコメントが付いた行) この時点で誤りです。resの結果はrecur_array_index([4], 0) + recur_array_index([], 1)の結果です(右辺が空のリストなので、足しても左辺と同じになっているだけです)。
sequelanonymous

2021/09/20 08:40

>そんな動作はしません。左辺と右辺の値が揃わないと足し算は行われませんので、「足し算の結果をresへ代入する」という処理も行いようがありません。 コメントありがとうございます。上の回答者への解答と同じになってしまいますが、 私もそう思っていましたが、`print文`で出力したresの値をみるとそう実行されているようにみえるのですが、どうこのprint文結果を解釈すればいいでしょうか?
maisumakun

2021/09/20 08:43

> 、`print文`で出力したresの値をみるとそう実行されているようにみえるのですが どこがそう見えたのですか? (上にコメントしたように、右辺が空のリストを返した場合、足し算をしても結果は左辺と同じなので、足したのか足していないのか区別が付きません)
sequelanonymous

2021/09/20 08:43

時間差の解答になってしまいました。下記の結果を誤解していたようです。空が返らないと勝手に勘違いしていました。ありがとうございます。 In [55]: nums=[4] In [56]: nums[1:] Out[56]: []
sequelanonymous

2021/09/20 08:51

すみません、そこでその後もわからずにいるんですが、return したresの結果`[[]]`はどこにいってしまうのでしょうか? ``` res: [[]] ------------------> 左側の関数の返り値と右側の関数の返り値の足し算結果が返る。 nums: [4], num: [], i: 2 -----> ??? ```
maisumakun

2021/09/20 08:55

> return したresの結果`[[]]`はどこにいってしまうのでしょうか? 足し算をするために、右辺の算出が終わるのを待っています。
maisumakun

2021/09/20 09:01

「a = foo() + bar()」のような式を書いた場合、「foo()を実行する→bar()を実行する→足し算を行う→結果をaに代入する」と進んでいきますが、今回の動作もこれと全く同じで、たまたまfooやbarが同じ関数だっただけです。
sequelanonymous

2021/09/20 09:02

つまり、下記のような考えで大方あっていますでしょうか? ``` res = recur_array_index(num, i-1) + recur_array_index(nums[1:], i) print(f"res: {res}") ---> print文までは処理が実行される return res. ---> ここでとまっていている、内部メモリで保存され、値が更新されている?で上の右辺の計算終了後に最後に更新された値を出力する?! ```
maisumakun

2021/09/20 09:06 編集

そうではありません。printまで行けばreturnします(「呼んだ側」でどうなっているかはまた別問題です)。
sequelanonymous

2021/09/20 09:11

> printまで行けばreturnします。 私もそこまではそう思っていました。そのあと、returnした[[]]はどこにいくのでしょうか?待っているという意味がわからずにいます。returnしているのだから、待っていないのでは?と思ってしまいました。 上のアウトプットに書かれている通り、print文の結果は出力されているので[[]]がどこにいったのかが理解できずにいます。
maisumakun

2021/09/20 09:13

> returnしているのだから、待っていないのでは?と思ってしまいました。 res = recur_array_index(num, i-1) + recur_array_index(nums[1:], i)とありますが、「左辺」の実行が終われば「右辺」の実行に移る、それだけの話です。 右辺の実行が終わるまで、左辺の値は足し算するために積まれています。
sequelanonymous

2021/09/20 09:13

>「呼んだ側」でどうなっているかはまた別問題です 内部でstackしていると理解しているのですがあっていますでしょうか?
maisumakun

2021/09/20 09:14

そんな感じです。
sequelanonymous

2021/09/20 09:58 編集

> 内部でstackしていると理解 >res = recur_array_index(num, i-1) + recur_array_index(nums[1:], i)とありますが、「左辺」の実行が終われば「右辺」の実行に移る、それだけの話です。 ご確認ありがとうございます。 上記2つのことが正しいと理解をすすめたとして、次のステップはどう解釈できますでしょうか? ``` res: [[]] ------------------> res = [[]] + [] の結果が出力されている nums: [4], num: [], i: 2 -----> 左側の関数結果は上で出力されstackに蓄積されているおり、一個前の引数の値に戻るが、なぜ、iだけカウントアップされている? res: [] --------------------> nums: [4], num: [], i: 2 を引数として、res = [] + [] の結果が出力される。 ```
maisumakun

2021/09/20 11:13 編集

nums: [4], num: [], i: 2は、nums: [3, 4], num: [4], i: 2の右辺で呼び出されたものです。 > なぜ、iだけカウントアップされている? 何が疑問なのかわかりません。同じ階層から呼び出す左辺と右辺で、iの値は異なります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問