静かなる名辞

pythonとプログラミングのこと



共有渡しと参照の値渡しと

はじめに

 関数やメソッドに引数を渡す方法は、一般的には

  • 値渡し
  • 参照渡し

 の2通りがあると認知されている。

 ところで、『参照の値渡し』という言葉も(ほぼ日本語Web圏限定で)存在する。これは「いわゆる『参照渡し』は参照自体を書き換えるんじゃなくて、参照する対象を変えるだけだから、そう呼んだ方が適当だよ!」という思想に基づくもの、だと思う。このページを見るとわかりやすい。

キミの言語は参照渡しできる?

 つまり、こういうことができたら『参照渡し』で

a = "hoge"
b = "fuga"
swap(a, b)
print(a, b)  # => fuga, hoge

 できなかったら「キミの言語は『参照渡し』できないよ、キミが『参照渡し』だと思っているのは『参照の値渡し』だよ!」ということか。言いたいことはわかる。

 上のリンクにはC言語で同様のことをやる方法としてこういう例が載っているが、

#include <stdio.h>

void swap(char *a, char *b){
  char tmp;
  tmp = *a;
  *a = *b;
  *b = tmp;
}

int main(void){
  char x = 'A', y = 'B';
  swap(&x, &y);
  printf("%c\n", x);
  printf("%c\n", y);
  return 0;
}

 参照する相手を書き換えて実現するのはなんか違う感じがする(それこそ『参照の値渡し』じゃん)。やるならこっちではないか。

#include <stdio.h>

void swap(char **a, char **b){
  char *tmp;
  tmp = *a;
  *a = *b;
  *b = tmp;
}

int main(void){
  char x = 'A', y = 'B';
  char *xptr, *yptr;
  xptr = &x;
  yptr = &y;
  swap(&xptr, &yptr);
  printf("%c\n", *xptr);
  printf("%c\n", *yptr);
  return 0;
}

 このコードを見ていると「あれ、じゃあ『参照渡し』の本当の名前は『参照の参照の値渡し』なの?」という疑問が生まれてくるが、本題から逸れるので追求はしない*1

共有渡しについて考える

 さて、『参照の値渡し』とよく似た概念として、『共有渡し』がある。

 ちなみに、日本語wikipediaの「引数」ページには『共有渡し』は存在しない代わり参照の値渡しがあり、英語版wikipediaの「Evaluation strategy」ページには『Call by sharing』*2はあって参照の値渡しはない。なんだかなぁ。

引数 - Wikipedia
Evaluation strategy - Wikipedia

 『共有渡し』が何者かというと、これは英語版wikipediaのページを読むのがわかりやすいのだが、一行引用してくると

also referred to as call by object or call by object-sharing

 ということであり、つまりは関数(メソッド)の呼び出し元と呼び出し先で同じオブジェクトを「共有」する方法である。

 僕のようなpython使いにとっては「それ普通じゃね?」なのだが、確かに値渡しとも『参照渡し』とも(何を示す言葉かは不問として)異なる概念と言われればそんな気はする。この言葉は、1974年、CLUという初期のオブジェクト指向言語とともに生み出された言葉だそうな。

 これに関連して、こんな議論もある。comp.lang.python *3の昔の議論らしい。pythonの呼び出しモデルは『Call by sharing』だと結論が着いている感じ。

Call By Object

 なんか、同じものを呼ぶ名前がいっぱいある。

The most accurate description is CLU’s “call by object” or “call by sharing“. Or, if you prefer, “call by object reference“.

 こんなに呼び方が多いのはちょっと酷いんだが、“call by object reference“あたりだと言いたいことはよく伝わってくる。

どっちが良いのか

 べつに『参照の値渡し』≒『共有渡し』とみなしても良いのだが、言葉のニュアンスは違うし、他にも考えるべきことがあって「どっちを使うか、あるいはどっちでも良いのか」という問題には答えを出しづらいと思うのだ・・・。

 C言語に対して『共有渡し』を使うのには躊躇する。まあ、メモリ領域を「共有」するのは同じという考え方もあるだろうけど。

  • あくまでも値渡し+参照渡し的な世界観で説明しようとする『参照の値渡し』と、オブジェクトが呼び出し元・先で「共有」されるという現象を重視する『共有渡し』

 同じ現象でも見方が違うのだと思う。
 この違いは意外と効いてきて、前者の立場を取るとJavaは「基本的に値渡し。プリミティブ型はそのまま値で渡るが、それ以外はアドレスの値が渡る」と値渡し的な世界観で説明できるが、後者の立場を取ると「プリミティブ型の値渡し、配列の『参照の値渡し』、オブジェクトの『共有渡し』の折衷」という苦しい説明にせざるを得ないと思う(配列に対して『共有渡し』の言葉を使うことを許すなら、真ん中は削れるけど)。Java使いの人たちが「Javaは値渡し!」と主張したがるのは、つまるところそういうことだろう*4
 逆にpythonrubyのような「すべてがオブジェクト」な言語では、プリミティブ型やら何やらのことは考える必要はなく、また言語仕様の表面で参照の値(要するにアドレス)が見えてくる訳でもないので、『共有渡し』の方がすっきりすると思う。

  • 認知度とか言葉としての良し悪し

 なんか、どっちもどっちという気がする。
 単純な好みの問題だと思うけど強いて選ぶとすれば、国際的に(一応は)通用するであろうこと、言葉の良し悪しについて議論の余地が少ない*5ことから、共有渡しの方が筋は良さそう

 まあ、個人的には『共有渡し』の方がスッキリするし、好きです。でも、『参照の値渡し』が絶対に駄目かというと別にそんなことはないと思うので、TPOをわきまえて使い分けるということで、どうですか。

まとめ

 ややこしくない(動作としてはよくわかる)けど、ややこしい(名前がうまく決められない)話だよね。

*1:けど、こういう動作を無条件に参照渡しと呼んでしまうのは、必ずしも良い考え方ではないと思う。だいたいこれの実装方法なんて、いくらでもあるんだしさ

*2:念のため書いておくと、「渡し」に対応するのは「Pass by」、「Call by」は「呼び出し」に対応するのだが、どちらにせよ意味は大して変わらないのでどちらで訳しても問題はない。この記事では日本語圏で一般的な「渡し」で統一している

*3:筆者はこれが何なのかはよくわからないが

*4:個人的には「Cみたいに明示的にアドレス渡す訳でもないのに値渡しって呼ぶのは逆説的でわかりづらいよ」と思うのだが、そのコストに勝るメリットがあるとする考えなのだろう

*5:というか議論になっていない程度に流行っていないだけかも・・・