У меня есть этот код:
$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" }
Я попал сюда случайно, и вопрос ФП привлек мое внимание. К сожалению, я не понимаю ни одного из объяснений сверху. Кажется мне, как всем это известно, получает, подхватывает, просто не может объяснить.
К счастью, чистое предложение из документации PHP по foreach делает это совершенно ясным:
Предупреждение: Ссылка на
$value
и последний элемент массива остаются даже после цикла foreach. Рекомендуется уничтожить его unset ().
Этот вопрос содержит много объяснений, но нет четких примеров того, как решить проблему, вызванную этим поведением. В большинстве случаев вам, вероятно, понадобится следующий код в вашем проходе по ссылке foreach
.
foreach ($array as &$row) { // Do stuff // Unset unset($row); }
с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] (и наоборот), поэтому он не может присваивать значение самому себе, поэтому он сохраняет предыдущее присвоенное значение 🙂
Потому что, если вы создаете ссылку на переменную, все имена этой переменной (включая оригинал) ставят ссылки.