擬似言語の手続と関数の違いとは?引数・戻り値と変数の有効範囲を解説
手続と関数、何が違うのか分からないまま解いている。
擬似言語の問題で、○から始まる一行目を読み飛ばしていませんか?
あの行には、その問題を解くための情報がぎゅっと詰まっています。飛ばして読み進めると、後半で必ず迷子になります。
私はエンジニアとして10年以上、Webシステムの開発や運用に携わってきました。実務で他人が書いた処理を読むときも、まず引数と戻り値だけを見て役割をつかみます。
中身を全部読むのは、そのあとです。この順番は、科目Bでもそのまま使えます。
この記事では、手続と関数の違いから、引数の渡し方、変数の有効範囲までを順に整理していきます。
手続と関数はどこが違うのか¶
まずは言葉の意味をはっきりさせましょう。違いは、たった一つです。
値を返すかどうか。ここだけです。
関数は計算を頼まれて答えを持ち帰る係、手続は仕事を頼まれて黙ってやってくる係。そう考えると、役割の差がつかみやすくなります。
どちらも処理をひとかたまりにして名前を付けたもの、という点では同じです。違いを並べると、次のようになります。
| 項目 | 手続 | 関数 |
|---|---|---|
| 値を返すか | 返さない | 返す |
| 宣言の書き方 | ○手続名(引数) | ○型名: 関数名(引数) |
| return | 無い、または値を伴わない | 値を伴って必ずある |
| 呼び出し方 | それだけで一行になる | 式の途中に置ける |
| よくある役割 | 表示、並べ替え、更新 | 計算、判定、探索 |
見分け方は宣言の一行目にある¶
見分けるコツは単純で、○のすぐ後ろを見るだけです。型名とコロンが続いていれば関数、いきなり名前が始まっていれば手続です。
実際に並べてみます。上が関数、下が手続です。
○整数型: goukei(整数型の配列: data) /* 整数型の値を返す=関数 */
○hyouji(文字列型: message) /* 何も返さない=手続 */
goukei のほうは、返ってくる値が整数型だと最初から分かります。この一行を読んだ時点で、末尾に return があると予想できるわけです。
一方の hyouji は、呼んでも何も返ってきません。だから答えの計算に使うのではなく、途中で何かをする役だと分かります。
呼び出し方も変わってくる¶
返す値があるかどうかは、呼ぶ側の書き方にも表れます。ここが読み取りのヒントになります。
関数は値になるので、代入の右辺や条件式の中に置けます。手続は値にならないので、単独の行として書かれます。
整数型: wa ← goukei(data) /* 関数なので、右辺に置ける */
hyouji("処理が終わりました") /* 手続なので、この行で完結する */
もし空欄補充で、代入の右辺が空いていたとします。そこに入るのは値を返すもの、つまり関数の呼び出しだと絞り込めます。
この視点だけで、選択肢が半分に減ることもあります。擬似言語の記号や書き方をまとめて確認したい場合は、早見表も役に立ちます。
【関連記事】擬似言語の記号・記述形式一覧|試験前のチェック早見表
引数は値のコピーとして渡される¶
次は引数です。ここは、科目Bで最も差がつくところだと感じています。
引数とは、呼び出す側から呼ばれる側へ渡す値のことです。渡された側は、その値を自分の変数として受け取ります。
大事なのは、渡されるのがコピーだという点です。呼ばれた側で値を書き換えても、呼び出した側の変数は変わりません。
仮引数と実引数は別の変数¶
言葉が2つ出てきますが、難しく考える必要はありません。宣言に書かれているほうが仮引数、呼ぶときに実際に渡すほうが実引数です。
次のコードで確かめてみましょう。
○baizou(整数型: n)
n ← n × 2
/* ここでの n は、この手続だけの変数 */
整数型: kazu ← 5
baizou(kazu)
/* 呼び出しのあとも kazu は 5 のまま */
baizou の中で n は10になります。ですが呼び出し元の kazu は、5のままです。
n と kazu は名前が違うだけでなく、置かれている場所そのものが別だからです。ここを混同すると、答えが二倍ずれます。
初心者のうちは、手続に入るところで値が写し取られる、とイメージしてください。写した紙をいくら書き換えても、元の紙は変わりません。
配列を渡すときは様子が違う¶
ただし、配列には注意が必要です。配列を引数にした場合、呼ばれた側での書き換えが呼び出し元にも及ぶ形で出題されます。
配列は中身そのものではなく、置き場所への案内が渡されると考えると納得しやすくなります。案内を頼りに同じ場所を触るので、変更が共有されるわけです。
問題文にどちらの扱いか書かれていることもあるので、まずは設問の注記を確認してください。書かれていなければ、配列は共有、単純な数値はコピーと考えるのが安全です。
配列そのものの読み方に不安が残る場合は、添字と要素数の整理を先に済ませておくと理解が早まります。
【関連記事】擬似言語の配列と添字がわからない人へ|要素数・二次元配列の読み方を解説
戻り値は呼び出した場所に置き換わる¶
続いて戻り値です。return が出てきたときに何が起きるのかを、はっきりさせておきましょう。
return に書かれた値は、関数を呼び出した場所そのものに置き換わります。呼び出しの式が、返ってきた値に化けるイメージです。
具体例で見てみます。配列の合計を求める、よくある形の関数です。
○整数型: goukei(整数型の配列: data)
整数型: wa ← 0
整数型: i
for (i を 1 から data の要素数 まで 1 ずつ増やす)
wa ← wa + data[i]
endfor
return wa
data が {2, 5, 8} なら、返るのは15です。そして呼び出し側の goukei(data) という部分が、そのまま15に置き換わります。
だから wa ← goukei(data) は、実質 wa ← 15 と同じ意味になります。ここが腑に落ちると、関数を含む式もこわくありません。
return に出会ったらそこで処理は終わる¶
もう一つ、覚えておきたい性質があります。return が実行された瞬間、その関数の処理は終わります。
下にまだ行が残っていても、実行されません。ここを見落とすと、通らないはずの処理まで追ってしまいます。
途中に return がある形は、探索の問題で頻繁に出ます。見つかった時点で答えを返してしまう、という書き方です。
○整数型: sagasu(整数型の配列: data, 整数型: target)
整数型: i
for (i を 1 から data の要素数 まで 1 ずつ増やす)
if (data[i] = target)
return i /* 見つけた時点で、ここで終わる */
endif
endfor
return -1 /* 最後まで無ければ -1 を返す */
target が最初の要素と一致すれば、ループは一周もせずに終わります。最後の return -1 まで進むのは、どこにも見つからなかったときだけです。
戻り値の追い方が曖昧なままだと、関数が自分を呼ぶ形でつまずきます。再帰に苦手意識がある人は、あわせて確認しておくと理解がつながります。
【関連記事】基本情報科目Bの再帰呼び出しがわからない人への対策方法を解説|擬似言語で処理を追うコツ
変数の有効範囲を取り違えない¶
ここからは、変数がどこまで届くのかという話です。有効範囲、あるいはスコープと呼ばれます。
手続や関数の中で宣言した変数は、その中だけで生きています。処理が終われば消えるので、外から見ることはできません。
これを局所変数といいます。先ほどの wa や i が、これにあたります。
大域変数が出てきたときの読み方¶
一方で、プログラム全体から使える変数もあります。宣言の先頭に大域と書かれているものです。
大域: 整数型: kaisu ← 0
○kasan(整数型: n)
kaisu ← kaisu + n /* 手続の外にある変数を書き換えている */
kaisu は、どの手続からも読み書きできます。呼ばれるたびに値が積み上がっていく、という動きになります。
ここが引数との決定的な違いです。引数はコピーなので元に影響しませんが、大域変数は本体そのものを触ります。
問題文で大域という文字を見つけたら、その変数の値がどこで変わるかを全体から探してください。一か所だけを見ていると、必ず食い違います。
同じ名前の変数が2つ出てきたら¶
やっかいなのは、大域変数と局所変数に同じ名前が使われている場合です。出題者は、ここを狙ってきます。
原則として、近いほうが優先されます。手続の中で同じ名前が宣言されていれば、その中では局所変数のほうが使われます。
見分ける手がかりは、宣言があるかどうかです。手続の中に型名付きの宣言があれば、それは新しく作られた別の変数です。
3つの違いを整理しておきます。
| 種類 | 宣言の場所 | 届く範囲 | 処理が終わると |
|---|---|---|---|
| 局所変数 | 手続や関数の中 | その中だけ | 消える |
| 仮引数 | 宣言の丸括弧の中 | その中だけ | 消える |
| 大域変数 | プログラムの先頭 | どこからでも | 残る |
表の右端に注目してください。値が残るかどうかが、大域変数を使う理由そのものです。
同じ手続を何度も呼んで結果をためたいとき、局所変数では毎回リセットされてしまいます。だから大域に置く、という設計になっているわけです。
呼び出しが重なる問題の追い方¶
最後に、手続どうしが呼び合う問題の読み方を扱います。ここまで理解できていれば、あとは追い方の型を持つだけです。
こうした問題では、いま自分がどの手続の中にいるのかを見失いがちです。そこで、呼び出しの順番を紙に残します。
たとえば、main から keisan を呼び、その中でさらに goukei を呼ぶとします。追い方は次のような形になります。
| 順番 | 場所 | していること | 渡した値 | 返ってきた値 |
|---|---|---|---|---|
| 1 | main | keisan を呼ぶ | data | 待ち |
| 2 | keisan | goukei を呼ぶ | data | 待ち |
| 3 | goukei | 合計を計算して返す | ― | 15 |
| 4 | keisan | 受け取って割り算 | ― | 5 |
| 5 | main | 結果を受け取る | ― | 5 |
上から下へ呼び出しが進み、下から上へ値が戻ってくる。この往復の形が見えれば、もう混乱しません。
待ちと書いた行がポイントです。呼び出し元は止まって待っているだけで、値が返るまで先へは進みません。
変数の値そのものを追う練習は、トレース表とあわせて行うと定着が早くなります。
【関連記事】擬似言語のトレース表の書き方|科目Bで変数の値を追う手順を解説
手を動かして確かめるのが一番早い¶
読んで納得しても、実際に動かすとまた違う発見があります。とくに引数のコピーと大域変数の違いは、目で見ると一瞬で腑に落ちます。
Giji Academy の擬似言語シミュレーターでは、手続を呼んだときに変数の中身がどう変わるかを、一行ずつ確かめられます。自分の予想と実行結果を見比べてみてください。
ずれた箇所が、そのまま次に復習すべき場所になります。よかったら学習ページからのぞいてみてください。
よくある間違いと直し方¶
私が受験生から相談を受ける中で、繰り返し出てくるつまずきがあります。まとめて確認しておきましょう。
| つまずき | 起きること | 直し方 |
|---|---|---|
| ○の行を読み飛ばす | 何を返すのか分からない | 型と引数だけ先に読む |
| 引数の書き換えが元に及ぶと思う | 値が二重に変わる | コピーだと考える |
| return の後ろも追う | 通らない処理を数える | return で線を引く |
| 大域変数を局所だと思う | 値がリセットされる前提になる | 大域の文字を探す |
| 呼び出し元に戻るのを忘れる | 途中で答えを出す | 戻り先を紙に残す |
一番多いのは、最後の行です。呼ばれた側の処理を追い終えたところで満足して、戻ってきた値を使う処理を忘れてしまうのです。
呼び出しは往復だと覚えてください。行って終わりではなく、必ず帰ってきます。
科目Bは20問を100分で解く試験です。一問あたりに使える時間は限られていますが、宣言の一行目を読むのに10秒もかかりません。
その10秒が、後半の数分を節約してくれます。
まとめ¶
手続と関数の違いは、値を返すかどうかという一点でした。宣言の○の後ろを見れば、すぐに見分けられます。
引数は、原則として値のコピーが渡されます。呼ばれた側で書き換えても、呼び出し元には影響しません。
戻り値は、呼び出した場所そのものに置き換わります。そして return に出会えば、そこで処理は終わります。
大域変数だけは例外で、どこからでも触れて、処理が終わっても値が残ります。同じ名前が出てきたら、宣言の場所を確かめてください。
ここまで押さえれば、複数の手続が絡む問題も、順番に追えるようになります。難しく見えるコードほど、実は役割分担がはっきりしているものです。
擬似言語そのものの成り立ちから振り返りたくなったら、基礎の記事にも寄ってみてください。
【関連記事】擬似言語とは?IT初心者にもわかる意味・書き方・プログラミングとの違いをやさしく解説
次に問題を開いたら、まず○の行だけを読んでみてください。それだけで、コードの見え方が変わるはずです。