再帰呼び出しの戻り値について、くだらない質問をさせていただきます。
再帰呼び出しの解説で、以下のような階乗の計算が使われると思います。
引数を5から渡していきますが、1になるまでは、条件分岐のfalseの処理が続つため、プロシージャ nの階乗 が呼び出され続けます。
引数が2までは、戻り値が呼び出し元に戻らないのではないでしょうか?
1になって、ようやく、戻り値として呼び出し元に戻り、そこで、終わるわけではないのでしょうか?
だとすると、下記のようなコードだと、必ず1が戻り値になるのではないかと思ってしまいます。
どうして、1までいって、1だけ戻って終わらず、2 6 24 が戻るんでしょうか?
超ド素人の質問でお恥ずかしいのですが、どなたか、ご教授願います。
コード ```Sub main() Dim answer As Long answer = nの階乗(5) Debug.Print answer End Sub Function nの階乗(n As Long) As Variant If n = 1 Then nの階乗 = 1 Else nの階乗 = n * nの階乗(n - 1) End If Debug.Print n & "の階乗は" & nの階乗 End Function
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
再帰処理はいろんなイメージの仕方があると思うので一つの例として回答いたします。
「Functionを呼べば戻り値が1つ返ってくる」ということを念頭に置くと良いかもしれません。
下は再帰のイメージです。
nの階乗(5) └ 5 * nの階乗(4) └ 4 * nの階乗(3) └ 3 * nの階乗(2) └ 2 * nの階乗(1) └ 1
nの階乗
というFunctionは再帰処理によって合計5回呼び出されます。処理の順番はもちろんnの階乗(5)
→nの階乗(4)
→nの階乗(3)
→nの階乗(2)
→nの階乗(1)
ですね。
そしてそれぞれのFunctionで戻り値が返ってきます。戻り値が返ってくる順番はnの階乗(1)
→nの階乗(2)
→nの階乗(3)
→nの階乗(4)
→nの階乗(5)
です。上のイメージでいうと処理が上から下に流れて下から上に戻り値が返る…そんな感じです。
1になって、ようやく、戻り値として呼び出し元に戻り、そこで、終わるわけではないのでしょうか?
ここの戻り値1
はあくまで nの階乗(1)
の戻り値です。
その後も処理は続き
nの階乗(2)
に対して戻り値2
が返り、
nの階乗(3)
に対して戻り値6
が返り、
nの階乗(4)
に対して戻り値24
が返り、
nの階乗(5)
に対して戻り値120
が返るとなるわけです。
なので nの階乗(5)
を呼び出すと結果として120
が返ってくるわけです。
(追記) 最初に書いたイメージをコードっぽく書いてみますね。
もとのFunctionは下のような形でした。
Function nの階乗(n As Long) As Variant If n = 1 Then nの階乗 = 1 Else nの階乗 = n * nの階乗(n - 1) End If Debug.Print n & "の階乗は" & nの階乗 End Function
例としてn = 2
のときは下のようになります。
n = 2 If n = 1 Then // ここには入らない nの階乗 = 1 Else // ここに入る nの階乗 = n * nの階乗(n - 1) End If Debug.Print n & "の階乗は" & nの階乗
このときnの階乗(n - 1)
の部分をバラして書くと下のようになります。
n = 2 If n = 1 Then // ここには入らない nの階乗 = 1 Else // ここに入る nの階乗 = n * { // nの階乗(1)の処理 n' = 1 If n' = 1 Then // ここに入る n'の階乗 = 1 Else // ここには入らない n'の階乗 = n' * nの階乗(n' - 1) End If Debug.Print n' & "の階乗は" & n'の階乗 // ここで「1の階乗は1」と出る } // {}の中身は n'の階乗 つまり 1 になる End If Debug.Print n & "の階乗は" & nの階乗 // ここで「2の階乗は2」と出る
もちろんこのコードは文法が誤っているので動かないのですが、気持ちとしては上のようなコードが走っているわけです。
n = 3
のときはこうです。
n = 3 If n = 1 Then // ここには入らない nの階乗 = 1 Else // ここに入る nの階乗 = n * { // nの階乗(2)の処理 n' = 2 If n' = 1 Then // ここには入らない n'の階乗 = 1 Else // ここに入る n'の階乗 = n' * { // nの階乗(1)の処理 n'' = 1 If n'' = 1 Then // ここに入る n''の階乗 = 1 Else // ここには入らない n''の階乗 = n'' * nの階乗(n'' - 1) End If Debug.Print n'' & "の階乗は" & n''の階乗 // ここで「1の階乗は1」と出る } // {}の中身は n''の階乗 つまり 1 になる End If Debug.Print n' & "の階乗は" & n'の階乗 // ここで「2の階乗は2」と出る } // {}の中身は n'の階乗 つまり 2 になる End If Debug.Print n & "の階乗は" & nの階乗 // ここで「3の階乗は6」と出る
nが大きくなると入れ子が増えるため書くのが大変になりますが同じように動きます。
投稿2021/04/25 12:18
編集2021/04/25 15:24総合スコア107
0
こちらで行デバックされると理解できませんか?
VBA
1Sub main() 2 Dim answer As Long 3 answer = nの階乗(5) 4 Debug.Print answer 5End Sub 6 7Function nの階乗(n As Long) As Variant 8 9 If n = 1 Then 10 nの階乗 = 1: Debug.Print n & " (戻り値=0 次計算渡し値=1)" 11 Else 12 xxxx = nの階乗(n - 1): Debug.Print n & " (戻り値=" & xxxx & " 次計算渡し値=" & n * xxxx & ")" 13 nの階乗 = n * xxxx 14 End If 15 'Debug.Print n & "の階乗は" & nの階乗 16 17End Function
投稿2021/04/28 05:40
総合スコア553
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
詳しい解説は他の方が丁寧にされているので、理解の一助として、質問者さんのコードを再起呼び出しをせずに書くと以下のようになります(n <= 5で決め打ち)
また、計算式の中に関数を入れているのも混乱のもとのように見えたので、一度変数に格納する書き方になっています。
VBA
1Sub main() 2 3Dim answer As Long 4answer = nの階乗(5) 5 6Debug.Print answer 7 8End Sub 9 10Function nの階乗(n As Long) As Variant 11 12If n = 1 Then 13 nの階乗 = 1 14Else 15 m = n1の階乗(n) 16 nの階乗 = n * m 17End If 18 19Debug.Print n & "の階乗は" & nの階乗 20 21End Function 22Function n1の階乗(n As Long) As Variant 23 24If n - 1 = 1 Then 25 n1の階乗 = 1 26Else 27 m1 = n2の階乗(n) 28 n1の階乗 = (n - 1) * m1 29End If 30 31Debug.Print n - 1 & "の階乗は" & n1の階乗 32 33End Function 34Function n2の階乗(n As Long) As Variant 35 36If n - 2 = 1 Then 37 n2の階乗 = 1 38Else 39 m2 = n3の階乗(n) 40 n2の階乗 = (n - 2) * m2 41End If 42 43Debug.Print n - 2 & "の階乗は" & n2の階乗 44 45End Function 46Function n3の階乗(n As Long) As Variant 47 48If n - 3 = 1 Then 49 n3の階乗 = 1 50Else 51 m3 = n4の階乗(n) 52 n3の階乗 = (n - 3) * m3 53End If 54 55Debug.Print n - 3 & "の階乗は" & n3の階乗 56 57End Function 58Function n4の階乗(n As Long) As Variant 59 60If n - 4 = 1 Then 61 n4の階乗 = 1 62Else 63 m4 = n4の階乗(n) 64 n4の階乗 = (n - 4) * m4 65End If 66 67Debug.Print n - 4 & "の階乗は" & n4の階乗 68 69End Function 70
大切なのは、プロシージャの途中で関数を呼びだしたとき、その関数の処理が終わったら、呼び出し元のプロシージャのに戻って処理が継続される、ということです。それは、再起呼び出しでも同様です。
投稿2021/04/27 00:19
総合スコア364
0
ベストアンサー
「再帰呼び出し」という特別な呼び出し方ではなく、単に同じ名前の関数を呼んだだけと考えてください。同じ関数を複数回呼び出しても、関数の実行ごとに引数などは別に確保されますので、特に干渉することはありません。
vb
1Function nの階乗(n As Long) As Variant 2 3If n = 1 Then 4nの階乗 = 1 5Else 6nの階乗 = n * nの階乗(n - 1) 7End If
nの階乗(6)
を呼んだ場合、コードはElse
の方に進んで、n * nの階乗(5)
という値を返します。当然、nの階乗(5)
の実行が終わらなければ結果の値は得られないので、戻ってきてから掛け算が行われます。
投稿2021/04/25 14:26
総合スコア146018
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/04/26 13:42
2021/04/26 14:02
2021/04/26 14:05
2021/04/27 01:28
2021/04/27 01:48
2021/05/29 22:14
0
関数「nの階乗」の呼出元はmainサブルーチン内の1箇所だけと誤解していませんか?
nの階乗内のelse文の「nの階乗 = n * nの階乗(n - 1)」はこの関数の復帰値を計算して設定する文です。
式の右辺にあるnの階乗は関数の呼び出しで、ここも呼出元です。
main関数とnの階乗内の2箇所にあるDebug.Printの結果をていねいに読んでください。
nの階乗という関数内のDebug.Printがn=5の時から順にn=1の時まで動作して、最後にmain関数のDebug.Printが動作しているのが出力フォーマットから分かると思います。
これは
mainサブルーチンはn=5で関数nの階乗を呼び出し、復帰するのを待つ → n=5で呼ばれた関数はn=4で関数nの階乗を呼び出し、復帰するのを待つ → n=4で呼ばれた関数はn=3で関数nの階乗を呼び出し、復帰するのを待つ → n=3で呼ばれた関数はn=2で関数nの階乗を呼び出し、復帰するのを待つ → n=2で呼ばれた関数はn=1で関数nの階乗を呼び出し、復帰するのを待つ → n=1で呼ばれた関数は1を復帰値として呼出元(n=2で呼ばれた関数)に復帰する n=2で呼ばれた関数はn*復帰値(=1)→2を復帰値として呼出元(n=3で呼ばれた関数)に復帰する n=3で呼ばれた関数はn*復帰値(=2)→6を復帰値として呼出元(n=4で呼ばれた関数)に復帰する n=4で呼ばれた関数はn*復帰値(=6)→24を復帰値として呼出元(n=5で呼ばれた関数)に復帰する n=5で呼ばれた関数はn*復帰値(=24)→120を復帰値として呼出元(mainサブルーチン)に復帰する answerに120が代入される
の結果です。
投稿2021/04/28 11:36
総合スコア1240
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/04/25 14:06
2021/04/25 15:26
2021/04/28 11:48
2021/05/29 22:13