質問するログイン新規登録

Q&A

解決済

8回答

1197閲覧

再帰関数の使い方でわからない記述がありました。

chanco22

総合スコア3

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

2クリップ

投稿2025/11/06 01:51

0

2

function test(n){
console.log('Hello');

if (n != 0) {
test(n - 1);
}

console.log('Bye');
}

test(2);

このコードでどうして、

Hello
Hello
Hello
Bye
Bye
Bye

とByeが3回続くのでしょうか。
console.log('Bye')を1度実行してtest関数は終了かと思ったのですが、Byeが3回続く理由がわかりません。

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

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

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

Lhankor_Mhy

2025/11/06 02:49

https://jsflow.info/?hl=ja などのJS実行ビジュアル化ツールを使うといいかもしれません。
chanco22

2025/11/12 02:38

初めてそのようなツールがあると知りました。ありがとうございます。使ってみます。
guest

回答8

0

このプログラムでは、test関数が自分自身を呼び出す「再帰」という仕組みを使っています。
具体的な流れを一つずつ説明します。

呼び出しの流れ

  1. 最初に test(2) が呼ばれます。
  2. 最初に console.log('Hello') が実行され、「Hello」と表示されます。
  3. n != 0 なので、test(1) がさらに呼ばれます。

今度は test(1) の中に入ります。

  1. ここでも「Hello」が表示されます。
  2. また n != 0 なので、今度は test(0) が呼ばれます。

次に test(0) の中に入ります。

  1. 再び「Hello」が表示されます。
  2. 今度は n == 0 なので、再帰呼び出しはありません。

この時点で初めて console.log('Bye') が実行され、「Bye」が出力されます。

test(0) の処理が終わると、ひとつ前の test(1) に戻ります。


test(1) に戻ったあとは、残っていた console.log('Bye') が実行されるので、また「Bye」が表示されます。その後、test(1) の処理も終わり、test(2) に戻ります。

最後に test(2) でも残っていた console.log('Bye') が実行され、再度「Bye」が表示されます。


図でまとめます

test(2) ├─ Hello ├─ test(1) │ ├─ Hello │ ├─ test(0) │ │ ├─ Hello │ │ ├─ Bye <-- ここで1回目のBye │ ├─ Bye <-- ここで2回目のBye ├─ Bye <-- ここで3回目のBye

ポイント

  • test 関数は、まず自分自身を呼び出し(再帰)、呼び出しから“戻ってきてから”「Bye」を出力する作りになっています。
  • これにより、関数の呼び出しが終了(戻る)するたびに、その時の「Bye」が出力されます。
  • このため、3回呼び出されているので「Bye」も3回出力される仕組みです。

なぜ「Bye」が複数回出るのか?

「test」関数は、呼び出されるたびに必ず最後で「Bye」を出力します。
再帰によって何度も呼ばれているため、終了したときの回数だけ「Bye」が出力される、ということになります。


もし他にも気になることがあれば、どうぞご質問ください。

投稿2025/11/08 14:03

gipcompany

総合スコア54

chanco22

2025/11/12 02:30

すごく丁寧なご回答をいただきありがたいです。 ありがとうございます。
guest

0

ベストアンサー

こんにちは。

可能な限りシンプルに説明してみます。
test 関数の処理を言葉で表すと、

  1. まず Hello を出力し、
  2. (条件が合えば) test を呼び出し、
  3. 最後に Bye を出力する

という一連の処理だと言えます。
つまり、test が呼び出されると、必ず Hello と Bye がペアで出力されるわけですね。
そして、その Hello と Bye の間に test 関数が呼ばれることがあるので、そこでも Hello と Bye のペアが出力されることになります。
なので、test 関数を再帰で呼んでいる限り、「Hello と Bye は同じ個数出力される」ことが分かるのです。

もしかしたら、test 関数の実装を「条件が合えば test 関数を呼び出す、そうでなければ Bye を出力する」と読み違えたのではないかなと勝手に想像しました。

投稿2025/11/06 03:16

tamoto

総合スコア4392

chanco22

2025/11/12 02:32

こちらの勘違いを言葉にしていただけて、腑に落ちました。まさにそのような認識でした。 とても分かりやすかったです。ありがとうございます。
guest

0

関数は console.log('Bye') を実行したら終了しますが、その関数を呼び出した親の関数はまだ終了していません。親の関数も自分の console.log('Bye') を実行する必要があります。これが「スタック」と呼ばれる仕組みで、関数呼び出しは後入れ先出し(LIFO)で処理されます。

1. test(2) 開始 ├─ console.log('Hello') → "Hello" 出力 ├─ n=2なので test(1) を呼び出す │ │ 2. test(1) 開始 │ ├─ console.log('Hello') → "Hello" 出力 │ ├─ n=1なので test(0) を呼び出す │ │ │ │ 3. test(0) 開始 │ │ ├─ console.log('Hello') → "Hello" 出力 │ │ ├─ n=0なので再帰呼び出しはしない │ │ └─ console.log('Bye') → "Bye" 出力 │ │ 3. test(0) 終了 ← ここで test(1) に戻る │ │ │ └─ console.log('Bye') → "Bye" 出力 │ 2. test(1) 終了 ← ここで test(2) に戻る │ └─ console.log('Bye') → "Bye" 出力 1. test(2) 終了

投稿2025/11/06 04:56

neko_the_shadow

総合スコア2431

chanco22

2025/11/12 02:31

「スタック」や「後入れ先出し」などの言葉を使っていただけたのでだいぶイメージがクリアになりました。 ありがとうございます。
guest

0

こんな感じでnをあわせて表示するとわかりやすいかもしれません

javascript

1function test(n){ 2 console.log(`Hello ${n}`); 3if (n != 0) { 4 test(n - 1); 5} 6console.log(`Bye ${n}`); 7} 8test(2);

子ネストから抜けたあと親のbyeに処理が流れる感じですね

投稿2025/11/06 02:11

yambejp

総合スコア118219

chanco22

2025/11/12 02:33

ありがとうございます。解決いたしました。
guest

0

もし下記のようにreturn;していれば、質問者のような動作になると思います。

php

1function test(n){ 2 console.log('Hello'); 3 4 if (n != 0) { 5 test(n - 1); 6 return; 7 } 8 9 console.log('Bye'); 10}

投稿2025/11/11 00:23

hide0128

総合スコア249

chanco22

2025/11/12 02:34

他の方と違ったアプローチで回答いただけて、さらに理解が深まりました。ありがとうございます。
guest

0

再帰というのは現在の処理を中断して関数内の最初の命令に戻ることを繰り返す動作を指すので、毎回残りの処理が”蓄積”されます
これはtestを他の関数に置き換えれば理解できるでしょう

function test(n){ console.log('Hello'); if (n != 0) { console.log(n - 1); } console.log('Bye'); }

’Bye’が表示されるのはif内のconsole.logが実行された後です
ではこのconsole.logが内部で複雑な演算を行い、実行が終了しない場合はどうでしょうか
当然’Bye’の表示は遅延します

再帰ではこれと同等のことが起きています
再帰の各ステップで、if内のtestは演算を完了していません
そしてifの後には’Bye’が待ち構えています

この中断がn!=0を満たしてifをスキップし、testの演算を完全に終了させるまでに2回起きているので、蓄積された各ステップの消費毎にconsole.log(‘Bye’)が実行されます

投稿2025/11/06 17:02

nanashi123

総合スコア150

chanco22

2025/11/12 02:34

「蓄積」という言葉がすごくわかりやすかったです。 ありがとうございます。
guest

0

自分は再帰関数はスタックを強く頭に思い浮かべます。
言語化するならスタックされた処理は途中でふわっと消えることはありません。

書き起こすときは樹形図を書いて
左から右へ進み、returnで右から左に戻るように一つ一つ追います。

二重再帰ではないのでシンプルですが、
関数としては、

Helloを出力して右へ(右からこちらに戻ってきたらByeを出力) -> Helloを出力して右へ(右からこちらに戻ってきたらByeを出力) -> Helloを出力してByeを出力

というように、左の処理が右に行って最後まで行ったら左に一つずつ戻ります。
(これを樹形図と呼ぶのか知りませんが)

投稿2025/11/06 04:57

編集2025/11/06 05:00
u2025

総合スコア132

chanco22

2025/11/12 02:36

他の方とは違ったイメージの説明で、さらに理解が深まりました。「戻る」というのがイメージしやすかったです。ありがとうございます。
guest

0

以前、同じ構造の再帰関数の動作の流れを図示したことがあります。
C言語だし、関数名が違う、具体的な表示の仕方が違う…けれど関数の構造は同じです。見比べてください。
C言語の関数再帰に関する疑問への私の回答

処理の流れをイメージしてもらおうという回答は既にいくつかありますが、イメージの仕方は一つではないように思います。私のような描き方もある、ということで参考にしていただきたく、付け加えます。

投稿2025/11/11 00:56

rubato6809

総合スコア1384

chanco22

2025/11/12 02:41

図示されていてわかりやすかったです。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.29%

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

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

質問する

関連した質問