我有一些关于在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 a3 callback
foreach ($this->a1 as &$v)
实现$v
还有另一个要迭代的元素?$a3
在第三个循环foreach ($a3 as &$x)
期间有效,但仅当数组以多个元素开始时才会起作用?我意识到在迭代期间修改数组可能不是一个好主意,但是因为PHP似乎允许它,我很好奇为什么上面的工作方式就像它一样。
答案 0 :(得分:4)
有趣的观察:
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
这意味着:
foreach
循环对数组的副本进行操作,循环内数组的任何修改都不会影响循环foreach
被强制使用原始数组,但在分配键和值变量后,在每次迭代之前使数组指针前进。还有一些优化会在指针到达结束时阻止另一次检查。因此,在最后一次迭代开始时,循环被告知再次运行然后完成 - 不再有可能干扰。while
循环each()
使数组指针前进,就像foreach
那样,但在最后一次迭代后再次显式检查for
循环,其中数组指针在每次迭代后高级,显然在任何时候更改数组都没有问题。答案 1 :(得分:3)
1.为什么第一个循环foreach($ this-&gt; a1 as&amp; $ v)没有实现$ v有另一个要迭代的元素?
该行为看起来是由于每个foreach迭代上的内部指针在数组上前进。在数组的 last 迭代中将数组元素添加到数组的末尾,即内部指针已经为空时,意味着不会迭代此元素。通过对代码的一些修改,可以看到这一点。
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.为什么在第三个循环foreach($ a3为&amp; $ x)期间修改$ a3工作,但仅当数组以多个元素开始时才会生效?
当然,如果在内部指针返回null之前向数组添加元素,那么元素将被迭代。在您的情况下,如果数组有一个元素,那么在第一次迭代时,内部指针已经返回null。但是,如果最初有多个元素,那么可以在第一次迭代时添加附加元素,因为此时内部指针将指向第二个初始元素。