この記事の内容
「foreachで回した要素自体の値を更新したい」といったことが、たまにあるかと思います。
(あるよね...?)
上記の要件を満たしたいと思った時に
・参照渡しを使う
・foreachのkeyを使う
大きく分けて上記2種類の方法があると思ってるのですが、個人的にはkeyを使用した方法を使いたいなと感じたので、その辺りの方法とメリデメを残しておこうと思います。
間違ってる点あれば、ご指摘頂ければ幸いです。
環境
$ php -v
PHP 7.1.33 (cli) (built: Oct 31 2019 17:37:57) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies
ダメなパターン
まずおさらいですが、普通にforeachで値をそのまま代入しても値は変わりません。
foreachを使用すると、元の値のコピーが作られる為です。
$tests = ["a" => 1, "b" => 2, "c" => 3];
foreach ($tests as $test) {
$test *= 2;
}
print_r($tests);
Array
(
[a] => 1
[b] => 2
[c] => 3
)
参照渡しで更新するパターン
変数の前に「&」を付ける事で、参照渡しになります。
通常の値渡しと違う点は、その名の通り「参照」が渡されるので、元の値を直接参照できること。
個人的には、ショートカットみたいなイメージを持ってます🤔
foreachの各要素を更新する場合は、asの後で指定する変数に&を付ける。
$tests = ["a" => 1, "b" => 2, "c" => 3];
foreach ($tests as &$test) {
$test *= 2;
}
print_r($tests);
# 結果
Array
(
[a] => 2
[b] => 4
[c] => 6
)
ただし、これだと最後の参照が残りっぱなしになるので、
$testを更新したりすると、最後に参照したkeyが"c"の要素が更新されます。
$tests = ["a" => 1, "b" => 2, "c" => 3];
foreach ($tests as &$test) {
$test *= 2;
}
$test = 100;
print_r($tests);
# 結果
Array
(
[a] => 2
[b] => 4
[c] => 100
)
この問題を回避するためには、参照を解除するために「unset」を使うと良い。
公式にも丁寧に書いてますね。
foreach ($tests as &$test) {
$test *= 2;
}
unset($test);
$test = 100;
print_r($tests);
# 結果
Array
(
[a] => 2
[b] => 4
[c] => 6
)
まぁ、$testという変数は作られちゃってるんですがw
参照渡しはあまり使いたくない
上記のようにすれば更新はできるんですが、個人的には参照渡しは好きじゃない。
・「&」一個付いているだけで挙動が変わるので、見逃しがち
・改修が難しくなる
・処理追うのめんどくさい
・unsetを忘れてバグを生む可能性が微レ存
バグが生まれやすいですし、「この変数は参照渡し」ってずっと自分の脳のメモリが圧迫されながら処理を追うのは結構しんどいです。
ということで、Keyで更新する方法をオススメしたい。
keyで更新するパターン
$tests = ["a" => 1, "b" => 2, "c" => 3];
foreach ($tests as $key => $value) {
$tests[$key] *= 2;
}
print_r($tests);
# 結果
Array
(
[a] => 2
[b] => 4
[c] => 6
)
個人的にはこっちのが読みやすい...。
上記パターンだと「$value」がいらない子になっちゃってるので、特に必要ない場合は「array_keys」を使うことで更にスッキリします。
$tests = ["a" => 1, "b" => 2, "c" => 3];
foreach (array_keys($tests) as $key) {
$tests[$key] *= 2;
}
print_r($tests);
# 結果
Array
(
[a] => 2
[b] => 4
[c] => 6
)
うん、読みやすくなりました。
ただし、真偽の程は定かじゃないけどこのやり方は参照渡しに比べるとパフォーマンス面では劣るそう。
普通に考えたら、値のコピーを作って処理する分参照渡しの方が早いのはそうなんだろうなぁ。
とはいえ、結構変わってくるなら業界的にも「大きめの処理では出来るだけ参照渡しを使いましょう」みたいな話が出回ってそうなので、気にする程ではないのかなとw
それよりも、初学者にも玄人にも読みやすいかつバグを生みづらいコードである事の方がメリットとして大きいと思ってます🤔
まとめ
参照渡し
・パフォーマンスは良い(はず)
・コードが読みづらい
・バグを生みやすい
key
・パフォーマンスは参照渡しより劣る(はず)
・読みやすい
ざっくりですが、こんな感じ。
この辺りの使い分けについて、明確にコーディングルールで定められている会社の方とかいらっしゃれば、ぜひ理由をお聞きしたいです!