PHPで配列の代入は参照ではなくコピー

来年からPHPも触ることになりそうなので、いろいろメモっていく予定。とりあえずPHPは良く知らないので、教科書は最近出たばかりで評判の良いパーフェクトPHPにするつもりだけどまだ届いていない。

PHPで書くと

さて、いきなり配列でハマってビビった。変数に代入すると参照ではなくコピー。常識っすか? 完全にオブジェクト脳だったのでこれはかなりインパクトがあった。

<?php
$a = array('hoge');
array_push($a, 'fuga');
array_push($a, 'piyo');
$b = $a;
$a[1] = 100;
var_dump($b);
var_dump($a);
array(3) {
  [0]=>
  string(4) "hoge"
  [1]=>
  string(4) "fuga"
  [2]=>
  string(4) "piyo"
}
array(3) {
  [0]=>
  string(4) "hoge"
  [1]=>
  int(100)
  [2]=>
  string(4) "piyo"
}

参照したい場合は「&」でポインタを渡す。

<?php
$a = array('hoge');
array_push($a, 'fuga');
array_push($a, 'piyo');
$b = &$a; // &で渡す
$a[1] = 100;
var_dump($b);
var_dump($a);
array(3) {
  [0]=>
  string(4) "hoge"
  [1]=>
  int(100)
  [2]=>
  string(4) "piyo"
}
array(3) {
  [0]=>
  string(4) "hoge"
  [1]=>
  int(100)
  [2]=>
  string(4) "piyo"
}

こんなのわかっててもハマりそうで、初っ端から嫌な予感しまくり。可能なかぎりRubyで書きたい。が現場では難しそう・・・。

Rubyで書くと

今後はPHPがらみのエントリーには、できるだけ同じコードをRubyでも書いてみるようにする。そうすれば、Rubyで書きたい欲求を少しでも満たせそう。Rubyはデフォルトが参照。今まで触った言語はすべてこの挙動だった。

a = ['hoge']
a.push 'fuga'
a.push 'piyo'
b = a
a[1] = 100
p a, b
["hoge", 100, "piyo"]
["hoge", 100, "piyo"]

なので、配列をコピーしたい場合にはいつもと違う書き方をする必要がある。

a = ['hoge']
a.push 'fuga'
a.push 'piyo'
b = a.clone   # コピーを代入
a[1] = 100
p a, b
["hoge", 100, "piyo"]
["hoge", "fuga", "piyo"]

dupとcloneはともに浅い(シャロウ)コピーだが、ちょっとだけ挙動が違うみたい。特異クラスやfrozen情報もコピーしてくれるcloneを使ったほうが無難っぽいかな? 深い(ディープ)コピーについてはまた今度。