基本情報科目Bの再帰呼び出しがわからない人への対策方法を解説|擬似言語で処理を追うコツ
基本情報技術者試験の科目Bを勉強していると、再帰呼び出しという言葉に出会うことがあります。
再帰呼び出しと聞くと、急に難しいアルゴリズムの話に感じますよね。
「関数の中で同じ関数を呼び出す」
と説明されても、最初は何を言っているのかわからないかもしれません。
でも安心してください。
再帰呼び出しは、考え方を分ければそこまで怖くありません。ポイントは、処理が深く進む流れと、答えが戻ってくる流れを分けて見ることです。
この記事では、IT初心者でも理解しやすいように、再帰呼び出しとは何か、どうやって処理を追えばよいのかを解説します。
再帰呼び出しとは何か¶
再帰呼び出しとは、関数や手続が、自分自身を呼び出す処理のことです。
短くいうと、同じ関数をもう一度使って、問題を少し小さくしながら解く方法です。
たとえば、factorialという関数の中で、またfactorialを呼び出すような処理です。これが再帰呼び出しです。
言葉だけだと不思議に感じるかもしれません。自分で自分を呼び出すとは、どういうことなのでしょうか。
イメージとしては、大きな問題を少し小さな同じ形の問題に分けていく処理です。
たとえば、5の階乗は次のように考えられます。
5の階乗 = 5 × 4の階乗
4の階乗 = 4 × 3の階乗
3の階乗 = 3 × 2の階乗
2の階乗 = 2 × 1の階乗
1の階乗 = 1 × 0の階乗
0の階乗 = 1
このように、同じ形の計算が少しずつ小さくなっていきます。
再帰呼び出しは、この小さくしていく流れをプログラムで表したものです。
基本情報科目Bで再帰呼び出しが難しく見える理由¶
再帰呼び出しが難しく感じるのは、処理の流れが一方向ではないからです。
普通の繰返し処理なら、上から下へ進み、同じ場所を何回か回るだけです。ところが再帰では、関数が別の関数呼び出しとして積み重なります。
そして、途中で答えが出たら、今度は逆向きに戻ってきます。
この往復のイメージが持てないと、頭の中が混乱します。
初心者が再帰でつまずきやすいポイントを整理すると、次のようになります。
| つまずくポイント | 起きやすい混乱 | 見るべきポイント |
|---|---|---|
| 自分自身を呼び出す | 無限に続きそうに見える | 停止条件を見る |
| 呼び出しが重なる | 今どのnなのかわからない | nの値を表にする |
| returnが複数ある | どの値が返るかわからない | 戻る順番を見る |
| 計算が後で行われる | 途中結果が見えにくい | 深く進む流れと戻る流れを分ける |
再帰呼び出しを理解するには、いきなり全体を見ようとしないことが大切です。
まずは、いつ止まるのか。そして、止まった後にどう戻るのか。この2つに分けて考えましょう。
科目Bの擬似言語で見る再帰呼び出しのサンプル¶
ここからは、基本情報技術者試験の科目Bの擬似言語に寄せた形で、再帰のサンプルを見ていきます。
IPAの科目Bサンプル問題セットにも、階乗を求めるfactorialという関数が登場します。関数の中でfactorial(n - 1)を呼び出す形です。
次のコードは、非負の整数nの階乗を返す関数です。
○整数型: factorial(整数型: n)
if (n = 0)
return 1
endif
return n × factorial(n - 1)
このコードの意味は、次のようになります。
nが0なら1を返す。そうでなければ、n × factorial(n - 1)を返す。
とても短いコードですが、再帰の基本が詰まっています。
再帰呼び出しは停止条件から見る¶
再帰呼び出しで最初に見るべきなのは、停止条件です。
停止条件とは、再帰呼び出しを終わらせる条件のことです。今回の例では、次の部分が停止条件です。
if (n = 0)
return 1
endif
nが0になったら、もうfactorialを呼び出しません。そこで1を返して終わります。
もし停止条件がなければ、関数は自分自身を呼び出し続けてしまいます。これは無限ループに近い危険な状態です。
再帰呼び出しの問題を見たら、まず停止条件を探しましょう。ここが見つかると、かなり安心できます。
引数が停止条件に近づいているかを見る¶
次に見るのは、引数が小さくなっているかです。
今回のコードでは、factorial(n - 1)となっています。つまり、呼び出すたびにnが1ずつ小さくなります。
nが5なら、次は4です。次は3、2、1、0と進みます。
最終的に停止条件のn = 0にたどり着きます。
再帰呼び出しでは、停止条件に近づいているかがとても重要です。停止条件があっても、そこに近づいていなければ処理は終わりません。
再帰呼び出しの処理を表で追ってみよう¶
ここからは、factorial(3)を呼び出した場合を追ってみます。
初心者のうちは、頭の中だけで追わないほうがいいです。表にして、どの呼び出しがどの値を返すのかを見えるようにしましょう。
まず、深く進む流れは次のようになります。
| 呼び出し | nの値 | 処理 |
|---|---|---|
| factorial(3) | 3 | 3 × factorial(2)を返そうとする |
| factorial(2) | 2 | 2 × factorial(1)を返そうとする |
| factorial(1) | 1 | 1 × factorial(0)を返そうとする |
| factorial(0) | 0 | 1を返す |
ここまでは、答えを計算し切っているわけではありません。まだ途中です。
factorial(0)で初めて、具体的な値である1が返ります。
次に、戻ってくる流れを見ます。
| 戻る処理 | 計算 | 戻り値 |
|---|---|---|
| factorial(0) | 1 | 1 |
| factorial(1) | 1 × 1 | 1 |
| factorial(2) | 2 × 1 | 2 |
| factorial(3) | 3 × 2 | 6 |
最終的に、factorial(3)の戻り値は6です。
再帰呼び出しは、深く進むときにはまだ計算が保留されています。停止条件に到達してから、戻りながら計算が確定します。
ここを分けて考えると、かなり読みやすくなります。
再帰呼び出しを追うときのコツ¶
再帰呼び出しの問題では、全部を一気に理解しようとすると混乱します。
特に、呼び出しの途中で戻り値まで考えようとすると、今どこにいるのかわからなくなります。
おすすめは、次の順番で見ることです。
- 停止条件を見る
- 引数がどう変わるかを見る
- 深く進む流れを書く
- 戻ってくる流れを書く
この順番を守るだけで、再帰はかなり追いやすくなります。
箇条書きにすると少し多く見えますが、やっていることはシンプルです。まず進む。次に戻る。この2段階です。
変数の値を追う練習が不安な方は、以下の記事を参考にしてください。
【関連記事】擬似言語の勉強方法とは?IT初心者でも無理なく身につく学習ステップを解説
もう一つの再帰呼び出しサンプル:1からnまでの合計¶
階乗だけだとイメージしにくい人もいるかもしれません。
次は、1からnまでの合計を求める再帰を見てみましょう。たとえば、sumTo(4)なら1 + 2 + 3 + 4なので10になります。
○整数型: sumTo(整数型: n)
if (n = 0)
return 0
endif
return n + sumTo(n - 1)
この関数も、考え方は階乗と同じです。
nが0なら0を返します。そうでなければ、n + sumTo(n - 1)を返します。
sumTo(4)を呼び出したときの進み方は、次のようになります。
sumTo(4) = 4 + sumTo(3)
sumTo(3) = 3 + sumTo(2)
sumTo(2) = 2 + sumTo(1)
sumTo(1) = 1 + sumTo(0)
sumTo(0) = 0
戻ってくると、次のように計算されます。
| 戻る処理 | 計算 | 戻り値 |
|---|---|---|
| sumTo(0) | 0 | 0 |
| sumTo(1) | 1 + 0 | 1 |
| sumTo(2) | 2 + 1 | 3 |
| sumTo(3) | 3 + 3 | 6 |
| sumTo(4) | 4 + 6 | 10 |
結果は10です。
階乗では掛け算でしたが、こちらは足し算です。形はほとんど同じですね。
再帰呼び出しは、処理の形を見抜けると理解しやすくなります。
再帰呼び出しと繰返しの違い¶
再帰呼び出しは、繰返し処理と似ています。
どちらも、同じような処理を何度も行うからです。ただし、考え方には違いがあります。
| 観点 | 再帰 | 繰返し |
|---|---|---|
| 処理の進み方 | 関数を呼び出しながら小さくする | ループで同じ処理を回す |
| 終了の考え方 | 停止条件に到達する | ループ条件が偽になる |
| 値の追い方 | 呼び出しと戻りを分ける | 変数の更新を順に追う |
| 苦手になりやすい点 | 戻る順番が見えにくい | 何回繰り返すか迷いやすい |
初心者にとっては、繰返しのほうが直感的です。
ただ、再帰には再帰の良さがあります。階乗、木構造の探索、分割して考える処理などでは、再帰で書くと流れが自然になることがあります。
基本情報技術者試験の科目Bでは、再帰呼び出しそのものを暗記するよりも、出てきたときに落ち着いて追えることが大切です。
私が再帰で意識していること¶
私はエンジニアとして10年以上コードを読んだり書いたりしてきましたが、再帰呼び出しはいまだに慎重に読みます。
経験者でも、複雑な再帰呼び出しを頭の中だけで追うのは危険です。少し条件が変わるだけで、呼び出しの順番や戻り値を勘違いすることがあります。
実務では、再帰呼び出しを見るときに必ず停止条件を確認します。次に、引数が停止条件に近づいているかを見ます。
これは試験でもまったく同じです。
初心者の方は、再帰呼び出しを一発で理解しようとしなくて大丈夫です。むしろ、表に書きながらゆっくり追うほうが正確です。
再帰呼び出しは、慣れるまでは不思議に見えます。でも、停止条件、深く進む流れ、戻ってくる流れの3つに分けると、少しずつ読めるようになります。
科目Bで再帰呼び出しが出たときの解き方¶
科目Bで再帰が出たときは、いきなり選択肢を眺めるより、まずコードの構造を見ましょう。
最初に探すのは、returnです。どの条件で何を返すのかを確認します。
次に、関数の中で同じ関数名が呼び出されている場所を探します。そこが再帰呼び出しです。
たとえば、factorial(n - 1)のような形です。
そして、引数がどう変わるかを見ます。n - 1なら、呼び出すたびに小さくなります。
ここまで見れば、あとは小さい値を入れて追うだけです。
再帰呼び出しは小さい値で試す¶
再帰呼び出しの問題では、いきなり大きな値で考えないほうがいいです。
factorial(10)を追うのは大変ですが、factorial(3)なら追えます。sumTo(100)は大きいですが、sumTo(4)なら表にできます。
本番でも、小さい値で考えると構造が見えます。
選択肢に式が並んでいる場合も、小さい値を代入して確かめると判断しやすくなります。再帰呼び出しは抽象的に考えるより、具体的な値で追うほうが理解しやすいです。
本番で焦りやすい方は、以下の記事もあわせて確認しておくと安心です。
【関連記事】擬似言語問題で焦る人への対策方法!|試験本番で頭が真っ白にならないためにどうすれば!?
再帰呼び出しでよくあるミス¶
再帰呼び出しの問題では、同じようなミスが起きやすいです。
まず多いのは、停止条件を見落とすことです。停止条件を見ないまま再帰呼び出しだけ追うと、どこで終わるのかわからなくなります。
次に多いのは、戻る順番を逆に考えてしまうことです。
factorial(3)では、最初に呼ばれるのはfactorial(3)ですが、最初に値を返すのはfactorial(0)です。この違いが大切です。
また、n × factorial(n - 1)のような式で、先に掛け算をしてしまうミスもあります。factorial(n - 1)の戻り値が決まってから、掛け算ができます。
再帰呼び出しは、途中の計算が保留されると考えると理解しやすいです。
再帰呼び出しを練習するときのおすすめ手順¶
再帰呼び出しを練習するときは、難しい問題から始めないでください。
最初は、階乗や1からnまでの合計のような短いコードで十分です。短いコードを確実に追えるようになってから、木構造や探索の問題に進みましょう。
練習では、必ず表を書きます。
コードを読んでわかった気になるのと、自分で表にできるのとでは大きな差があります。
おすすめの練習順は次の通りです。
| 順番 | 練習内容 | 目的 |
|---|---|---|
| 1 | 停止条件を探す | どこで終わるかを確認する |
| 2 | 引数の変化を見る | 停止条件に近づくか確認する |
| 3 | 小さい値で追う | 処理の形を理解する |
| 4 | 戻り値の表を書く | 戻る順番を理解する |
| 5 | 選択肢と照合する | 試験形式に慣れる |
この順番で練習すると、再帰への苦手意識はかなり減ります。
特に大切なのは、小さい値で追うことです。再帰は、n = 3やn = 4で考えるだけでも十分に仕組みが見えてきます。
まとめ:再帰呼び出しは進む流れと戻る流れに分けて考えよう¶
再帰呼び出しとは、関数や手続が自分自身を呼び出す処理です。
最初は難しく見えますが、考えるポイントは多くありません。停止条件、引数の変化、戻り値の順番を見ることが大切です。
基本情報技術者試験の科目Bで再帰が出たら、まず停止条件を探してください。次に、再帰呼び出しの引数が停止条件に近づいているかを確認します。
その後、小さい値で表を書き、深く進む流れと戻ってくる流れを分けて追います。
再帰呼び出しは、頭の中だけで理解しようとすると難しいです。表にすれば、かなり見えやすくなります。
私も、再帰は必ず分解して読みます。初心者のうちは、ゆっくりで大丈夫です。
いきなり難しい再帰問題を解ける必要はありません。まずはfactorial(3)やsumTo(4)のような小さい例を、自分の手で追えるようにしましょう。
それができれば、再帰呼び出しは怖いものではなくなります。
少しずつ、確実に理解していきましょう。