引数と戻り値のトレース
呼び出しの行で表が二階建てになる
前回、関数は呼ばれて初めて動くこと、引数は位置で対応することを確かめました。今回はそれを変数表で追います。
関数が出てくるトレースがむずかしく感じるのは、変数表に 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 つを守れば崩れません。
- 呼び出しの行では、引数の欄にだけ値を書き、代入先はまだ触らない
- 定義側を最後まで追い、return が返す値を余白に控える
- 呼び出しの行に戻り、控えた値を式にはめて代入し、関数の中の変数を消す
では、下のコードを 1 行ずつ追ってみてください。行番号は 1 行目の bai の定義から数えます。空行も 1 行として数えるので、main の定義は 6 行目です。
課題
- 各行を実行したあとの値を入れる
- 値が変わらない変数はそのままの値を入れる
- 関数が終わって消えた変数は — を入れる