Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Общие вопросы > foreach


Автор: race1 28.10.2008, 17:17
Код

foreach ($array as $cur_value) {
  ...
}


Как известно, переменная $cur_value не удаляется после выполнения цикла. Это понятно, но почему не работает такой же foreach если вызвать его просто второй раз? Т.е.

Код

$array = ('1', '2', '3');
foreach ($array as $cur_value) {
  print $cur_value;
}
// Выводит 1, 2, 3

foreach ($array as $cur_value) {
  print $cur_value;
}
// Выводит не 1, 2, 3, а, например, 1, 1, 3

foreach ($array as &$cur_value) {
  print $cur_value;
}
// Выводит 1, 2, 3


Просто интересно разобраться, почему так. Разве $cur_value во втором цикле не будет просто перезаписана? И почему правильно работает если использовать ссылку.

Вообще в реальном примере $array это массив массивов массивов, но думаю это не очень важно...

Спасибо.

Автор: HackMan 28.10.2008, 17:39
Цитата(race1 @  28.10.2008,  17:17 Найти цитируемый пост)
$array = ('1', '2', '3');

как-то удивительно, что оно у тебя вообще что-то выводит

Автор: teroni 28.10.2008, 17:41
Что-то вы намудрили... У меня этот код 123 три раза вывел... Только объявление массива неправильное конечно. У Вас действительно ЭТОТ код печатал во втором цикле 113?

Автор: HackMan 28.10.2008, 17:43
этот код кроме Parse error ничего не печатает вовсе

Автор: race1 28.10.2008, 17:51
Насчёт объявления массива это моя ошибка, надо конечно так:
Код

  $array = array('1', '2', '3');


Насчёт реального кода. Да, один foreach для вывода значений массива. В массиве 4 элемента, на выходе было два первых элемента, потом 3-й и 4-й. Сам массив что-то вроде этого:

Код

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => Personal Trainers
            [sub_categories] => Array
                (
                    [0] => Array
                        (
                            [id] => 5
                            [name] => Personal Trainers
                        )

                    [1] => Array
                        (
                            [id] => 6
                            [name] => Personal Training Studios
                        )

                )

        )

    [1] => Array
        (
            [id] => 2
            [name] => PRACTITIONERS
            [sub_categories] => Array
                (
                    [0] => Array
                        (
                            [id] => 10
                            [name] => Massage Therapists
                        )

                    [1] => Array
                        (
                            [id] => 11
                            [name] => Massage Therapy Clinic
                        )

                    [2] => Array
                        (
                            [id] => 12
                            [name] => Physiotherapists
                        )

                )

        )
...


Выводил $cur_value['id'] и $cur_value['name']

Автор: HackMan 28.10.2008, 17:59
 smile код в студию, только не выдуманный, а copy-paste из проекта. Чудес не бывает.

Автор: race1 28.10.2008, 18:42
Угу smile Хотя вообще это не так важно, если сразу нет идей почему, лучше просто дальше не копать, я буду делать unset псле каждого foreach.

Есть один файл, который подключает несколько других. Один из этих файлов - левое меню. Там из базы формируется массив который я скопипастил в предыдущем посте, и он выводится через foreach:

Код

    <?php
foreach ($categories as $cur_category) {
    ?>
            <li>
              <a href="#"><?php print htmlspecialchars($cur_category['cat_name']); ?></a>

              <ul>
    <?php
//... что-то не важное
    ?>
              </ul>
            </li>
      <?php
      }
?>


В другом файле мне опять надо вывести эти же категории, опять foreach и теже названия переменных:

Код

<?php
  foreach ($categories as $cur_category) {
?>
    <select id="sub_category_<?php print $cur_category['id']; ?>"
            name="sub_category_<?php print $cur_category['id']; ?>"
            class="hidden">
<?php
    foreach ($cur_category['sub_categories'] as $cur_subcategory) {
?>
      <option value="<?php print $cur_subcategory['id']; ?>"><?php print htmlspecialchars($cur_subcategory['name']); ?></option>
<?php
    }
?>
    </select>
<?php
  }
?>


Вот здесь-то и проявляется косяк, элементы массива выводились два раза первый, потом третий и 4-й.

Автор: Nigel 28.10.2008, 21:27
При каждой итерации цикла смещается внутренний указатель массива, однако foreach при запуске делает reset. Вероятно, у вас где-то ошибка в реализации.
По-моему, удобнее в вашем случае использовать http://ru2.php.net/manual/ru/function.array-walk.php или http://ru2.php.net/manual/ru/function.array-walk-recursive.php

Автор: sTa1kEr 29.10.2008, 09:51
race1, первые два цикла у вас в примере полностью рабочие, а вот последнем есть распространенная ошибка (думаю из-за этого у вас и проблемы):
Цитата(race1 @  28.10.2008,  18:17 Найти цитируемый пост)
foreach ($array as &$cur_value) {
  print $cur_value;
}

Нужно всегда помнить, что операция по присвоению жесткой ссылки всегда затрагивает обе переменные (они обе помечаются, как "переменная переданая по ссылке") до тех пор пока существует более одной ссылки на значение переменной. А приводит это к тому, что поведение таких переменных несколько отличается, а именно в данном случае это приводит к тому, что последний элемент в массиве будет передаваться по ссылке при копировании массива! Пример:
Код

$array = array(1, 2, 3);
foreach ($array as &$value)
{
   echo $value; // всего-лишь выводим... никакой мистики
}

var_dump($array);
/*
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  &int(3) -- амперсанд тут не с проста!
}
*/

$array2 = $array; // просто копирование
$array2[0] = 10;
$array2[1] = 10;
$array2[2] = 10;

var_dump($array);
/*
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  &int(10) -- значение изменилось, т.к. при копировании массива все его значения были копированы as is
}
*/

// из той-же оперы
function some($arr)
{
    // параметр передан не по ссылке, ожидаем, что все значения были скопированы
    $arr[0] = 100;
    $arr[1] = 100;
    $arr[2] = 100;
}
some($array2);

var_dump($array);
/*
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  &int(100) -- no comments
}
*/

Есть и еще ряд других проблем связанных с амперсандом, по этому крайне желательно избегать их использование. Но если это необходимо, то удалять сразу после использования:
Код

$array = array(1, 2, 3);
foreach ($array as &$value)
{
   $value++;
   unset($value);
}
// такой цикл полностью безопасен
var_dump($array);
/*
array(3) {
  [0]=>
  int(2)
  [1]=>
  int(3)
  [2]=>
  int(4)
}
*/

Автор: race1 30.10.2008, 15:51
Спасибо, стало понятнее! smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)