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

发布于 2024-11-05 01:25:14 字数 968 浏览 0 评论 0原文

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

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;
}

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

  1. 使用 rewind() 方法将内部索引变量设置为第一个元素的位置。
  2. 使用 valid() 方法测试是否已到达数组末尾。如果是这样,请退出。
  3. 使用 Iterator 接口的 key()current() 方法来检索数组当前元素的键和值。
  4. 使用 next() 方法将内部索引变量设置为紧邻当前元素之后的元素的位置。
  5. 转到 2。

至少,这是它与用户定义的类一起工作的方式。我不太确定它如何与内置数组类型一起工作。它会以同样的方式工作吗?我可以用 foreach 替换 for 行吗?

I have written the following function to flatten arrays:

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;
}

I would like to replace the for line with the simple foreach ($args as &$arg). On what basis? I once wrote a class that implements the Iterator interface, which is essentially the basis of how the foreach control structure works. If I remember correctly, what the foreach control structure does is the following:

  1. Use the rewind() method to set an internal index variable to the position of the first element.
  2. Use the valid() method to test whether the end of the array has already been reached. If so, exit.
  3. Use the key() and current() methods of the `Iterator interface to retrieve the key and value of the current element of the array.
  4. Use the next() method to set the internal index variable to the position of the element immediately after the current one.
  5. Goto 2.

At least, that is how it works with user-defined classes. I am not so sure about how it would work with the built-in array type. Would it work the same way? Can I replace the for line with the foreach?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风筝有风,海豚有海 2024-11-12 01:25:14

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

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

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

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

First, I should point out that, in general, modifying the contents of a collection while iterating over it is considered a "bad thing" -- many languages will throw an exception if you try. PHP is not one of those languages, however.

Two things are relevant here for your question:

First, when using arrays, PHP's foreach makes a copy of the array and iterates over that. In this case, you can safely modify the original array but your foreach won't see any of those changes. In your case that wouldn't work -- the new values you tack onto the end of $args won't come up in the foreach.

You can force PHP to use the original array by iterating over a reference to the array. In this case, the internal behavior will become relevant. PHP keeps an internal pointer to the "next" array element. If you change the contents of an array element that 'foreach' has already seen, you will not see the changes. If you change the contents of the array somewhere beyond the current element, you will see those changes. This should work for you, but I dunno if I'd trust it.

野侃 2024-11-12 01:25:14

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

关于代码需要指出的几点:
1)将 count($args) 放在 for 循环的第二个参数中意味着它会在循环中的每次迭代中进行处理,如果您有一个非常大的数组,这可能会很昂贵。

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

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

for and foreach are pretty interchangeable, but in this case, you cannot add elements to the args array and process them right away with the foreach, so your function will not function the same way if you use foreach.

A couple things to point out about the code:
1) putting the count($args) inside the 2nd parameter of the for loop means it's processed every iteration through the loop, which if you have a really large array can be expensive.

I would count the number of args before processing the loop, store it in a variable and use that in place of count($args) in the for arguments, then add 1 to the count every time you add a new element to the args array. This would be faster and use less memory.

2) This could be cleaned up to use recursion through functions, which would do the same thing without multiple loops and would use slightly less code.

滥情空心 2024-11-12 01:25:14
$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
)
*/
$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
)
*/

Using reference 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
)
*/
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文