У меня есть этот код:
$a = array ('zero','one','two', 'three');
foreach ($a as &$v) {
}
foreach ($a as $v) {
echo $v.PHP_EOL;
}
Может кто-нибудь объяснить, почему вывод: нуль один два два.
Из руководства по сертификации сертификации zend.
У меня есть этот код:
$a = array ('zero','one','two', 'three');
foreach ($a as &$v) {
}
foreach ($a as $v) {
echo $v.PHP_EOL;
}
Может кто-нибудь объяснить, почему вывод: нуль один два два.
Из руководства по сертификации сертификации zend.
Так как во втором цикле $v
по-прежнему является ссылкой на последний элемент массива, он каждый раз перезаписывается.
Вы можете видеть это так:
$a = array ('zero','one','two', 'three');
foreach ($a as &$v) {
}
foreach ($a as $v) {
echo $v.'-'.$a[3].PHP_EOL;
}
Как вы можете видеть, последний элемент массива принимает текущее значение цикла: "ноль", "один", "два", а затем он просто "два"...:)
Мне пришлось потратить несколько часов, чтобы понять, почему [3] меняется на каждой итерации. Это объяснение, по которому я приехал.
В PHP есть два типа переменных: обычные переменные и ссылочные переменные. Если мы назначим ссылку переменной на другую переменную, переменная станет ссылочной переменной.
например, в
$a = array('zero', 'one', 'two', 'three');
если мы делаем
$v = &$a[0]
0-й элемент ($a[0]
) становится ссылочной переменной. $v
указывает на эту переменную; поэтому, если мы внесем какое-либо изменение в $v
, оно будет отражено в $a[0]
и наоборот.
теперь, если мы делаем
$v = &$a[1]
$a[1]
станет ссылочной переменной, а $a[0]
станет нормальной переменной (поскольку никто другой не указывает на $a[0]
, он преобразуется в нормальную переменную. PHP достаточно умен, чтобы сделать его нормальной переменной, когда никто другой не указывает на это)
Это то, что происходит в первом цикле
foreach ($a as &$v) {
}
После последней итерации $a[3]
является ссылочной переменной.
Так как $v
указывает на $a[3]
любое изменение на $v
приводит к изменению на $a[3]
во втором цикле,
foreach ($a as $v) {
echo $v.'-'.$a[3].PHP_EOL;
}
в каждой итерации при изменении $v
изменяется $a[3]
. (потому что $v
все еще указывает на $a[3]
). Именно по этой причине $a[3]
изменяется на каждой итерации.
В итерации перед последней итерацией $v
присваивается значение 'two'. Так как $v
указывает на $a[3]
, $a[3]
теперь получает значение "два". Имейте это в виду.
В последней итерации $v
(которая указывает на $a[3]
) теперь имеет значение 'two', потому что $a[3]
было установлено на два в предыдущей итерации. two
. Это объясняет, почему "два" повторяются, когда $v печатается на последней итерации.
Первый цикл
$v = $a[0];
$v = $a[1];
$v = $a[2];
$v = $a[3];
Да! Ток $v
= $a[3]
.
Второй цикл
$a[3] = $v = $a[0], echo $v; // same as $a[3] and $a[0] == 'zero'
$a[3] = $v = $a[1], echo $v; // same as $a[3] and $a[1] == 'one'
$a[3] = $v = $a[2], echo $v; // same as $a[3] and $a[2] == 'two'
$a[3] = $v = $a[3], echo $v; // same as $a[3] and $a[3] == 'two'
поскольку $a[3]
назначается перед обработкой.
Я думаю, что этот код показывает процедуру более понятной.
<?php
$a = array ('zero','one','two', 'three');
foreach ($a as &$v) {
}
var_dump($a);
foreach ($a as $v) {
var_dump($a);
}
Результат: (Обратите внимание на последние два массива)
array(4) {
[0]=>
string(4) "zero"
[1]=>
string(3) "one"
[2]=>
string(3) "two"
[3]=>
&string(5) "three"
}
array(4) {
[0]=>
string(4) "zero"
[1]=>
string(3) "one"
[2]=>
string(3) "two"
[3]=>
&string(4) "zero"
}
array(4) {
[0]=>
string(4) "zero"
[1]=>
string(3) "one"
[2]=>
string(3) "two"
[3]=>
&string(3) "one"
}
array(4) {
[0]=>
string(4) "zero"
[1]=>
string(3) "one"
[2]=>
string(3) "two"
[3]=>
&string(3) "two"
}
array(4) {
[0]=>
string(4) "zero"
[1]=>
string(3) "one"
[2]=>
string(3) "two"
[3]=>
&string(3) "two"
}
Я попал сюда случайно, и вопрос OP привлек мое внимание. К сожалению, я не понимаю ни одного из объяснений сверху. Мне кажется, что все это знают, понимают, понимают, просто не могут объяснить.
К счастью, чистое предложение из документации PHP на foreach делает это совершенно ясным:
Предупреждение: Ссылка на
$value
и последний элемент массива остаются даже после цикла foreach. Рекомендуется уничтожить его unset().
В этом вопросе есть много объяснений, но нет четких примеров того, как решить проблему, вызванную этим поведением. В большинстве случаев вам, вероятно, понадобится следующий код в вашем проходе по ссылке foreach
.
foreach ($array as &$row) {
// Do stuff
// Unset
unset($row);
}
Это:
$a = array ('zero','one','two', 'three');
foreach ($a as &$v) {
}
foreach ($a as $v) {
echo $v.PHP_EOL;
}
совпадает с
$a = array ('zero','one','two', 'three');
$v = &$a[3];
for ($i = 0; $i < 4; $i++) {
$v = $a[$i];
echo $v.PHP_EOL;
}
ИЛИ
$a = array ('zero','one','two', 'three');
for ($i = 0; $i < 4; $i++) {
$a[3] = $a[$i];
echo $a[3].PHP_EOL;
}
ИЛИ
$a = array ('zero','one','two', 'three');
$a[3] = $a[0];
echo $a[3].PHP_EOL;
$a[3] = $a[1];
echo $a[3].PHP_EOL;
$a[3] = $a[2];
echo $a[3].PHP_EOL;
$a[3] = $a[3];
echo $a[3].PHP_EOL;
Я нашел этот пример также сложным. Почему во втором цикле на последней итерации ничего не происходит ($ v остается "два" ), это то, что $v указывает на $a [3] (и наоборот), поэтому он не может присваивать значение самому себе, поэтому он сохраняет предыдущее присвоенное значение:)
Потому что, если вы создаете ссылку на переменную, все имена для этой переменной (включая оригинал) СТАТЬ СПРАВОЧНИКАМИ.