ディープコピーとシャローコピー
いままでディープコピーとシャローコピーを逆に覚えていた。ので、覚書。最近流行りのポインタ関連のお話。
値の渡し方のパターン
簡単に端折ると、関数の引数として変数を与える場合、
- 値の値渡し(プリミティブ型変数の値をそのまま渡す)
- 値の参照渡し(プリミティブ型変数の参照を渡す)
- 参照の値渡し(参照型変数のもつ値を値として渡す)
- 参照の参照渡し(参照型変数自体の参照を渡す)
の4パターンを考えることができて、プログラム言語用語における『参照渡し(call-by-reference)』はパターン2とパターン4を指すのだけれど、パターン3を『参照渡し(call-by-reference)』と呼んでしまっている人が存在する、というお話。JavaScriptの配列に関しては、このパターン3に該当します。
http://rarihoma.hatenablog.com/entry/2012/01/09/233731
という感じで値の渡し方には4つのパターンがある。一般的言う値渡しは1,3のこと、参照渡しは2,4のこと。
[2012/06/16 02:00 追記]
c言語で1〜4を表すとどうなるかを自分でもCあんまり分かんないのに書いてみたけど、参照とポインタを混同してたみたいなので取り消し線引いておこうと思ったけど間違えて消した…
deep copyとshallow copyはどのパターン?
ディープコピーは値の値渡し、シャローコピーは参照の値渡しとなる。
名前から考えればわかりそうなもんだが、シャローコピーの場合は値を格納するメモリのアドレス値を渡すだけで、値自体のコピーは行わない。
ディープコピーは対象のメモリアドレス値を辿り、そのアドレスが指す値を丸々コピーする。
ディープコピーのが値丸々コピーするわけなのでシャローコピーに比べて重い処理になる。
shallow copyのわかりにくさ
function test(arg){ arg[0]++; } var arr = [0, 0]; console.dir(arr); // => [0, 0] test(arr); console.dir(arr); // => [1, 0]
このコード、直感的には最後のconsole.dirで [0, 0] が出力されそうなもんだが、[1, 0] が出力される。
関数への引数を渡す場合はシャローコピー(=参照の値渡し)になるから。
以下の場合は逆に最後のconsole.dirで [0, 0] が出力される。
上の場合は参照している配列のアドレスが指す値自体を変更しているために関数の外に影響が出ているけど、下の場合は配列のアドレス自体を変えているためにその影響は仮引数のある関数内に留められる。
function test(arg){ arg = [1, 0]; } var arr = [0, 0]; console.dir(arr); // => [0, 0] test(arr); console.dir(arr); // => [0, 0]
大抵の言語は値渡しらしい
だから値渡しのの中でも2つあるパターン区別するためにシャローコピー、ディープコピーなんて言葉ができたのかな?
参考:http://jp.rubyist.net/magazine/?0032-CallByValueAndCallByReference