引数と戻り値のトレース

呼び出しの行で表が二階建てになる

前回、関数は呼ばれて初めて動くこと、引数は位置で対応することを確かめました。今回はそれを変数表で追います。

関数が出てくるトレースがむずかしく感じるのは、変数表に 2 種類の変数が同時に並ぶからです。呼び出す側の変数と、呼び出される側の引数や局所変数です。この 2 つは別の場所にあり、片方を書き換えてももう片方は動きません。表の上では隣に並んでいても、別の部屋にある変数だと思ってください。

呼び出しの行は 2 回通る

もう 1 つの山場は、呼び出しの行を 2 回通ることです。

q ← bai(p)

この 1 行では、まず bai の中へ入っていき、bai が return で値を返してからこの行に戻ってきて、はじめて q への代入が起きます。行きの通過では q はまだ変わりません。ここを 1 回で済ませてしまうと、戻り値が入る前の値と入ったあとの値がごちゃまぜになります。

ですから、呼び出しの行はトレース表に 2 行ぶん書きます。行きの 1 行と、帰りの 1 行です。今回の演習でも、9 行目と 10 行目がそれぞれ 2 回出てきます。

引数が消えるところまで書く

関数が return で終わると、その関数の引数と局所変数は消えます。表では全角ダッシュに戻します。消したことを表に残しておくと、同じ関数をもう一度呼んだときに、前回の値が残っていると勘違いせずに済みます。

今回のコードでは bai を 2 回呼びます。1 回目で x が 3、y が 6 になり、いったん両方とも消え、2 回目で x が 4、y が 8 になります。この「消えてから入り直す」動きが見えていれば、同じ関数を何度呼んでも迷いません。

式の中に呼び出しがあるとき

q ← q + bai(4)

この行のように、式の一部に呼び出しが入ることもあります。順番は、先に bai(4) を求め、その戻り値を使って足し算をし、最後に q へ代入するという流れです。右辺をすべて計算し終えてから左辺へ入れる、という代入の原則は関数が入っても変わりません。

このとき右辺の q は、代入される前の古い値です。今回なら 6 です。左辺の q だけが新しくなります。

追いかたのまとめ

関数付きのトレースは、次の 3 つを守れば崩れません。

  1. 呼び出しの行では、引数の欄にだけ値を書き、代入先はまだ触らない
  2. 定義側を最後まで追い、return が返す値を余白に控える
  3. 呼び出しの行に戻り、控えた値を式にはめて代入し、関数の中の変数を消す

では、下のコードを 1 行ずつ追ってみてください。行番号は 1 行目の bai の定義から数えます。空行も 1 行として数えるので、main の定義は 6 行目です。

課題

  1. 各行を実行したあとの値を入れる
  2. 値が変わらない変数はそのままの値を入れる
  3. 関数が終わって消えた変数は — を入れる

ヒント

1 / 10 行目のトレース

1○整数型: bai(整数型: x)
2 整数型: y
3 y ← x × 2
4 return y
5
6○整数型: main()
7 整数型: p, q
8 p ← 3
9 q ← bai(p)
10 q ← q + bai(4)
11 return q
実行した行xypq
実行前
8 行目

8 行目を実行したあとの値を入れてください。変わらない変数は、 そのままの値を入れます。まだ値が入っていない変数は「」と入れます。