当前位置: 首页 > 知识库问答 >
问题:

在foreach()迭代期间修改数组

濮君植
2023-03-14

我有几个关于在foreach()循环期间修改数组的查询。在下面的代码中,我循环遍历三个包含闭包/回调的数组,并调用每个数组。在迭代过程中,我在每个数组的末尾追加了一个闭包,但是有时foreach()似乎无法识别数组已经改变了大小,因此追加的闭包不会被调用。

class Foo
{
    private $a1 = array();
    private $a2 = array();

    public function f()
    {
        echo '<pre style="font-size: 20px;">';
        echo 'PHP: ' . phpversion() . '<br><br>';

        $this->a1[] = function() { echo 'a1 '; };
        $this->a1[] = array($this, 'g');
        foreach ($this->a1 as &$v)
        {
            // The callback added in g() never gets called.
            call_user_func($v);
            //echo 'count(v) = ' . count($v) . ' ';
        }

        echo '<br>';

        // The same thing works fine with a for() loop.
        $this->a2[] = function() { echo 'a2 '; };
        $this->a2[] = array($this, 'h');
        for ($i = 0; $i < count($this->a2); ++$i)
            call_user_func($this->a2[$i]);

        echo '<br>';

        // It also works fine using a local array as long as it
        // starts off with more than one element.
        $a3[] = function() { echo 'a3 '; };
        //$a3[] = function() { echo 'a3 '; };
        $i = 0;
        foreach ($a3 as &$x)
        {
            call_user_func($x);
            if ($i++ > 1) // prevent infinite loop
                break;

            // Why does this get called only if $a3 originally starts
            // with more than one element?
            $a3[] = function() { echo 'callback '; };
        }

        echo '</pre>';
    }

    private function g()
    {
        echo 'g() ';
        $this->a1[] = function() { echo 'callback '; };
    }

    private function h()
    {
        echo 'h() ';
        $this->a2[] = function() { echo 'callback '; };
    }
}

$foo = new Foo;
$foo->f();

输出:

PHP: 5.3.14-1~dotdeb.0

a1 g() 
a2 h() callback 
a3

预期产出:

a1 g() callback
a2 h() callback 
a3 callback

的输出,如果我取消对循环之前的第二个元素的注释:

a3 a3 callback
    a1 as&ampv)/code>没有实现 有另一个要迭代的元素?/li> 在第三个循环 期间工作,但仅当数组从多个元素开始?/li>时才工作

我意识到在迭代期间修改数组可能不是一个好主意,但是既然PHP似乎允许这样做,我很好奇为什么上面的工作方式是这样的。

共有2个答案

施德元
2023-03-14

有趣的观察:

echo "foreach:  ";
$a = array(1,2,3);
foreach($a as $v) {
  echo $v, " ";
  if ($v===1) $a[] = 4;
  if ($v===4) $a[] = 5;
}

echo "\nforeach&: ";
$a = array(1,2,3);
foreach($a as &$v) {
  echo $v, " ";
  if ($v===1) $a[] = 4;
  if ($v===4) $a[] = 5;
}

echo "\nwhile:    ";
$a = array(1,2,3);
while(list(,$v) = each($a)) {
  echo $v, " ";
  if ($v===1) $a[] = 4;
  if ($v===4) $a[] = 5;
}

echo "\nfor:      ";
$a = array(1,2,3);
for($v=reset($a); key($a)!==null; $v=next($a)) {
  echo $v, " ";
  if ($v===1) $a[] = 4;
  if ($v===4) $a[] = 5;
}

结果在

foreach:  1 2 3 
foreach&: 1 2 3 4 
while:    1 2 3 4 5 
for:      1 2 3 4 5 

这意味着:

    循环对数组的副本进行操作,在循环中对数组的任何修改都不会影响loop/li> 具有引用值的 被强制使用原始数组,但在分配键和值变量后的每次迭代前推进数组指针。此外,还有一些优化正在进行,以防止在指针到达末尾时再次进行检查。因此,在最后一次迭代开始时,循环被告知再运行一次,然后完成--不可能有更多的干扰。/LI> 使用 循环与 一样推进数组指针,但在最后一次迭代后再次显式检查它/li> 循环,在每次迭代后数组指针都前进,显然在任何点更改数组都没有问题。/li>
西门旻
2023-03-14

为什么第一个循环foreach(this-&gta1 as&ampv)没有意识到v还有另一个元素要迭代

这种行为看起来是由于在每次foreach迭代时,内部指针在数组上被推进。在数组的最后一次迭代时,即当内部指针已经为null时,将数组元素添加到数组末尾,意味着不会迭代此元素。通过对代码的一些修改,可以看到这一点。

class Foo
{
    private $a1 = array();
    private $a2 = array();

    public function f()
    {
        echo '<pre style="font-size: 20px;">';
        echo 'PHP: ' . phpversion() . '<br><br>';

        $this->a1[] = function() { echo 'a1 <br/>'; };
        $this->a1[] = array($this, 'g');
        foreach ($this->a1 as $key => &$v)
        {
           //lets get the key that the internal pointer is pointing to 
           // before the call.
                  $intPtr = (key($this->a1) === null) ? 'null' : key($this->a1);
                echo 'array ptr before key ', $key, ' func call is ',    
                       $intPtr, '<br/>' ;
            call_user_func($v);
            //echo 'count(v) = ' . count($v) . ' ';
        }

        echo '<br><br>';

        // The same thing works fine with a for() loop.
        $this->a2[] = function() { echo 'a2 '; };
        $this->a2[] = array($this, 'h');
        for ($i = 0; $i < count($this->a2); ++$i)
            call_user_func($this->a2[$i]);

        echo '<br><br>';

        // It also works fine using a local array as long as it
        // starts off with more than one element.
        $a3[] = function() { echo 'a3 '; };
        //$a3[] = function() { echo 'a3 '; };
        $i = 0;
        foreach ($a3 as &$x)
        {
            call_user_func($x);
            if ($i++ > 1) // prevent infinite loop
                break;

            // Why does this get called only if $a3 originally starts
            // with more than one element?
            $a3[] = function() { echo 'callback '; };
        }

        echo '</pre>';
    }

    private function g()
    {
        echo 'g() <br>';
        $this->a1[] = function() { echo 'callback '; };
    }

    private function h()
    {
        echo 'h() <br>';
        $this->a2[] = function() { echo 'callback '; };
    }
}

$foo = new Foo;
$foo->f(); 

输出:

array ptr before key 0 func call is 1
a1 
array ptr before key 1 func call is null <-will not iterate over any added elements!
g() 

a2 h() 
callback 

a3

2.2为什么在第三个循环foreach(a3 as&ampx)期间修改a3起作用,但只有当数组开始时有多个元素时才起作用?

当然,如果在内部指针返回null之前向数组中添加一个元素,则该元素将被迭代。在您的情况下,如果数组有一个元素,那么在第一次迭代时,内部指针已经返回NULL。然而,如果最初有一个以上的元素,则可以在第一次迭代时添加额外的元素,因为此时内部指针将指向第二个初始元素。

 类似资料:
  • 我有一个json blob,如下所示: 这只是一个在这里发布的例子。在这个例子中,我们看到它提供了一个学校每年发生的所有事件的列表,该列表是一个累积列表,即上一年的事件也会被追加。我们知道的是,每年的活动都将以“会议开始”活动开始。我想通过这件事,以最新的一系列事件结束。请忽略事件名称中的时间戳或年份,它们只是示例,我在现实世界中没有此类信息。我需要的最终结果是: 所以,我想保留从“会话开始”事件

  • 问题内容: 假设我们有一个Python字典,我们正在像这样迭代它: (并且仅仅是一些黑盒转换。) 换句话说,我们尝试在使用对其进行迭代的同时向其中添加/删除项目。 这个定义好吗?您能否提供一些参考来支持您的答案? (很明显,如果损坏了该如何解决,所以这不是我所追求的角度。) 问题答案: 在python文档页面(针对2.7)上明确提到了 使用而添加或删除字典条目可能会产生一种或无法遍历所有条目。 对

  • 问题内容: 我知道您不应在遍历列表时添加/删除项目。但是,如果不更改列表长度,是否可以修改要迭代的列表中的项目? 还是应该迭代列表索引?像那样: 问题是:以上两种方式都是允许的,还是只有第二种是没有错误的? 如果答案是肯定的,以下代码段是否有效? UPD。我想在python文档中看到“允许这些操作”而不是某人的假设。 问题答案: 可以这么说,您 不是在 修改列表。您只是在修改列表中的元素。我不认为

  • 问题内容: 上面的python代码使输出与预期的完全不同。我想循环播放项目,以便在循环播放时可以跳过某个项目。 问题答案: 该代码试图在迭代列表时修改列表。我在任何情况下都不会这样做。 你可以使用运算符跳过列表中的每三个项目。 关于我的示例的其他要点与中的新语法有关。 我使用列表推导定义t,因为它在中有效(请参见下文) t是中的函数 现在,的行为类似于,但它适用于任意大小的值。后者不再存在。

  • 我正在运行一个pyspark脚本,在下面遇到了一个错误。由于我的代码“如果len(RDD.Take(1))>0:”,它似乎在说“RuntimeError:在迭代期间设置更改的大小”。我不确定这是不是真正的原因,不知道到底出了什么问题。任何帮助都将不胜感激。 谢谢! 第65行,在调用r=self.func(t,*rdds)文件“/usr/lib/spark/python/lib/pysspark.z

  • 如何在迭代中更改python迭代器? 例如: 此打印: 我想打印: 编辑:抱歉,这个问题让人们感到困惑。我对为什么当我在for循环中尝试更改迭代器时,for循环在下一次迭代中忽略它很感兴趣。 其他人已经提交了一个答案来清除我的困惑。范围创建一个列表,然后for循环将迭代器分配给列表中的下一个变量。

  • 上下文 我想迭代一个Spark数据集,并为每一行更新一个HashMap。 以下是我的代码: 问题 我的问题是Foreach根本不迭代,lambda从来没有执行过,我不知道为什么。 我在这里实现了它:如何在SparkJava中遍历/迭代数据集? 最后,尽管数据集不是空的,但所有内部向量都保持为空(初始化时)(查看给定代码示例中的第一个注释)。 我知道foreach从不迭代,因为我做了两个测试: 添加