当在循环内修改迭代的数组/集合时,foreach控制结构如何表现?

时间:2011-05-03 19:53:48

标签: php arrays iterator foreach

我编写了以下函数来展平数组:

function flatten() {
    $args  = func_get_args();
    $items = array();

    for ($i = 0; $i < count($args); $i++) {  // <-- (*)
        $arg =& $args[$i];

        if (is_array($arg))
            foreach ($arg as &$item)
                $args[] =& $item;
        else
            $items[] = $arg;
    }

    return $items;
}

我想用简单的for替换foreach ($args as &$arg)行。在什么基础上?我曾经写过一个实现Iterator接口的类,它基本上是foreach控制结构如何工作的基础。如果我没记错的话,foreach控制结构的作用如下:

  1. 使用rewind()方法将内部索引变量设置为第一个元素的位置。
  2. 使用valid()方法测试是否已到达阵列的末尾。如果是这样,请退出。
  3. 使用`Iterator接口的key()current()方法检索数组当前元素的键和值。
  4. 使用next()方法将内部索引变量设置为当前元素之后的元素位置。
  5. 转到2。
  6. 至少,这就是它如何与用户定义的类一起使用。我不太确定如何使用内置数组类型。它会以同样的方式工作吗?我可以将for行替换为foreach吗?

3 个答案:

答案 0 :(得分:2)

首先,我应该指出,一般来说,在迭代它时修改集合的内容被认为是“坏事” - 如果你尝试,许多语言会抛出异常。但是,PHP不是这些语言之一。

这里有两件事与您的问题相关:

首先,当使用数组时,PHP的foreach会复制数组并对其进行迭代。在这种情况下,您可以安全地修改原始数组,但您的foreach将不会看到任何这些更改。如果你的情况不起作用 - 你在$ args结尾处的新值将不会出现在foreach中。

您可以通过迭代对数组的引用来强制PHP使用原始数组。在这种情况下,内部行为将变得相关。 PHP保留一个指向“下一个”数组元素的内部指针。如果更改'foreach'已经看到的数组元素的内容,则不会看到更改。如果在当前元素之外的某处更改数组的内容,您将看到这些更改。 应该为你工作,但如果我信任它,我就不知道。

答案 1 :(得分:1)

for和foreach是可以互换的,但是在这种情况下,你不能将元素添加到args数组并使用foreach立即处理它们,所以如果你使用foreach,你的函数将不会以相同的方式运行。

有关代码的几点注意事项: 1)将计数($ args)放在for循环的第二个参数中意味着它在循环中每次迭代都被处理,如果你有一个非常大的数组可能很昂贵。

我会在处理循环之前计算args的数量,将它存储在变量中并在for参数中使用它来代替count($ args),然后在每次添加新元素时将数量加1 args数组。这样会更快并且使用更少的内存。

2)这可以被清理以通过函数使用递归,这将在没有多个循环的情况下执行相同的操作并且将使用稍微少的代码。

答案 2 :(得分:1)

$nums = array(1, 2, 3, 4);

$newNum = max($nums) + 1;
foreach ($nums as $num) {
    echo $num;
    if ($newNum > 10) {
        break;
    }
    $nums[] = $newNum++;
}

print_r($nums);

/* output
1234

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] => 6
    [6] => 7
    [7] => 8
)
*/

使用参考foreach ($nums as &$num)

/* output
1234567

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
    [4] => 5
    [5] => 6
    [6] => 7
    [7] => 8
    [8] => 9
    [9] => 10
)
*/