研究というほどではないが用語が・・・(call-by-value)

call-by-value、日本語では値渡し・・・と検索結果が出てくるが、実際にはもう一つある*1
別の意味がありそうに書いてあったサイトが一箇所だけあったが、「極めて明らかな」間違いだったので、ちょっと書いておこうと思った。


まず通常の値渡しの話。これはどこを検索しても出てくるのでそんなに長く話さない。
値渡しというのは、プログラミングにおいて関数呼び出し時に引数をどのように渡すかの話。
(関数に対して、サブルーチン、手続き、メソッドなど、似ているが厳密には異なる意味を持つ機能も多い。実情ほとんど同じなのでここではC言語に合わせて関数で統一。必要ならば読み替えてほしい。)
呼ばれた関数の中で引数を上書きしても、呼び出し元に影響しないこと。
プログラミング初心者が結構陥りがちなミスの一つに、引数へ値を代入して呼び出し元で影響がないことによるものがある。しばらくCを書かないと同様のミスをすることがある。
なお、影響する場合は参照渡しと呼ばれる(対応英語はcall-by-reference)。C言語には存在しないが、C++には存在する*2
広く使われているオブジェクト指向Javaは、「参照を値」渡しする。紛らわしいが、引数そのものを上書きしても呼び出し元の参照は変わらない。ただし、参照している先のデータは同じなので、参照先のデータがもつ参照を書き換えたら呼び出し元でも影響を受ける。


さて、もう一方のcall-by-valueとは何か。これは「評価規則」のことである。


上の値渡しは、「(引数に与えられたデータではなく)結果を持って呼び出す」ことを意味している。
一方、評価規則としてのcall-by-valueは「値にして呼び出す」ことを意味している。


もう少し説明しよう。
関数の呼び出し時に、次のようなコードを書いたとする。(相変わらずCである)
int inc(int t) { return t+1; }
int dec(int t) { return t-1; }
dec(inc(0));
これは「inc関数に引数0を与えて、返ってきた結果を引数にinc関数を呼び出す」ことを意味している。・・・まあ、この説明でわかる人は上のコードでわかるだろう。
さて、上の読み方はcall-by-valueを前提にしている。なぜなら、引数のinc関数を先に呼び出して「評価」しているからだ。
したがって、次のような評価順序で計算が行われる。
dec(inc(0)) → dec(0+1) → dec(1) → 1-1 → 0
これがcall-by-valueによる評価である。通常のCはこちら。


ではそうではない場合とは?たとえば次のような評価が考えられる。
dec(inc(0)) → inc(0)-1 → (0+1)-1 → 1-1 → 0
引数より先にdecのほうを展開している。このような評価をcall-by-nameと呼ぶ。
ちなみに、C言語ではマクロ関数(今はもうあまり使わないか?)がcall-by-nameにあたる。引数が展開されるので、副作用(変数の値の変更だと思ってよろしい)を持つコードは書かないほうがよい(というか書いてはいけない)ことになる。


派生版にcall-by-need*3というものもある。これは「値として利用せざるを得なくなった場合(たとえば条件分岐の条件文)に評価する。
Cにはこれにあたるものはない。(構造が複雑になる、副作用を伴う場合に評価されない可能性がある、などの理由による。)


実際の言語ではこのような評価規則は必ず一意になるように(ほかに選びようがないように)作られている(はず*4なの)で、利用者はあまり意識しない。
だからあまり知られないのだろうが・・・

*1:情報系の研究者はもう一方を思い浮かべることが多い。

*2:C++では引数に&をつけることで参照渡しになる。また、他系統の言語にはいくつかある。

*3:lazy-evaluation(遅延評価)とも呼ぶ。

*4:断言できないのは、たとえばCでは複数の引数のうちどれが最初に評価されるかが仕様で決まっていないため。もちろん処理系では一意なのだが・・・