Удалит. Но в цикле ты его все равно обработаешь.
У меня была задача такого плана: список структур, которые надо было сгруппировать по некому признаку. Получился вложенный цикл. Во внутреннем цикле я помечал элементы удаленными (сперва я тоже пытался делать unset), чтобы не обработать их повторно. Оказалось, что $list[$k] видит изменение значений, а foreach, в котором сейчас находишься, не видит.
Проблема решается как и в первом посте: использованием ссылок в цикле. И не забываем удалить ссылку после цикла.
Вариант без ссылок:
$list = array(
array('type' => 1, 'val' => 5),
array('type' => 2, 'val' => 10),
array('type' => 1, 'val' => -2),
array('type' => 3, 'val' => 20),
);
foreach ($list as $key => $item) {
echo "Fetch: {$item['type']}, {$item['val']}: ";
if (!empty($list[$key]['deleted'])) {
echo "skip\n";
continue;
}
echo "continue\n";
$list[$key]['deleted'] = 1;
foreach ($list as $key2 => $item2) {
if (!empty($list[$key2]['deleted']))
continue;
if ($item2['type'] != $item['type'])
continue;
$list[$key]['val'] += $item2['val'];
$list[$key2]['deleted'] = 1;
}
}
foreach ($list as $item)
printf("%1u %3d %1u\n", $item['type'], $item['val'], !empty($item['deleted']));
/*
Fetch: 1, 5: continue
Fetch: 2, 10: continue
Fetch: 1, -2: skip
Fetch: 3, 20: continue
1 3 1
2 10 1
1 -2 1
3 20 1
*/
Вариант с ссылками:
$list = array(
array('type' => 1, 'val' => 5),
array('type' => 2, 'val' => 10),
array('type' => 1, 'val' => -2),
array('type' => 3, 'val' => 20),
);
foreach ($list as &$item) {
echo "Fetch: {$item['type']}, {$item['val']}: ";
if (!empty($item['deleted'])) {
echo "skip\n";
continue;
}
echo "continue\n";
$item['deleted'] = 1;
foreach ($list as &$item2) {
if (!empty($item2['deleted']))
continue;
if ($item2['type'] != $item['type'])
continue;
$item['val'] += $item2['val'];
$item2['deleted'] = 1;
}
}
unset($item);
foreach ($list as $item)
printf("%1u %3d %1u\n", $item['type'], $item['val'], !empty($item['deleted']));
/*
Fetch: 1, 5: continue
Fetch: 2, 10: continue
Fetch: 1, -2: skip
Fetch: 3, 20: continue
1 3 1
2 10 1
1 -2 1
3 20 1
*/
Добавлено через 10 часов, 36 минут и 50 секунд:У меня получилось такое заключение:
Форма "foreach (list as val)" создает временную копию массива с копиями значений.
Форма "foreach (list as &val)" не создает копию массива со ссылками на исходные значения.Но проверка работы лимитов памяти не подтверждает, а еще больше запутывает.
Пример. В настройках memory_limit = 128M. Смысл теста в том, чтобы создать условия, когда на копию объекта не хватит памяти.
$memsize = 128 * 1024 * 1024;
$list = array(
str_repeat('1', $memsize * 0.4),
str_repeat('1', $memsize * 0.4)
);
foreach ($list as $v) {
echo strlen($v), "\n";
}
Как ни странно, тест проходит. А вот этот падает на строке 8.
$memsize = 128 * 1024 * 1024;
$list = array(
str_repeat('1', $memsize * 0.35),
str_repeat('1', $memsize * 0.35)
);
foreach ($list as &$v) {
echo strlen($v), "\n";
}
Добавлено через 3 часа, 24 минуты и 35 секунд:И вот еще интересный аспект влияния цикла на время жизни объекта.
class X
{
private $name;
public function __construct($name) {
$this->name = $name;
echo "++ $this->name\n";
}
public function __destruct() {
echo "-- $this->name\n";
}
}
$list = array(new X('old_1'), new X('old_2'));
foreach ($list as $k => $v) {
// $list[$k] = null;
$list[$k] = new X('new_' . ($k + 1), 0);
}
Строка 18 не влияет на результат: объекты будут уничтожены после цикла, по завершению программы:
++ old_1
++ old_2
++ new_1
++ new_2
-- old_1
-- old_2
-- new_1
-- new_2Скажу более: если обнуление заменить на unset(), то результат также не изменится.
class X
{
private $name;
public function __construct($name) {
$this->name = $name;
echo "++ $this->name\n";
}
public function __destruct() {
echo "-- $this->name\n";
}
}
$list = array(new X('old_1'), new X('old_2'));
foreach ($list as $k => &$v) {
// $list[$k] = null;
$list[$k] = new X('new_' . ($k + 1), 0);
}
А здесь влияет. Без "обнуления": создается новый объект, после чего уничтожается старый.
++ old_1
++ old_2
++ new_1
-- old_1
++ new_2
-- old_2
-- new_2
-- new_1С "обнулением": сперва уничтожается старый, потом создается новый.
++ old_1
++ old_2
-- old_1
++ new_1
-- old_2
++ new_2
-- new_1
-- new_2А если тут применить unset(), то присвоение в строке 19 добавляет элемент в массив в конец списка, что приводит к дополнительной итерации цикла, в которой добавляется еще один элемент и т.д. Т.е. с такой формой цикла массив можно использовать как самопополняемую очередь заданий.