PHP Foreach删除数组元素

5

今天我在为博客撰写的文本分析工具上工作时,发现PHP的行为对我来说非常奇怪,我无法理解。 在规范化文本时,我尝试删除长度低于最小长度的单词,因此我在我的规范化方法中编写了以下内容:

if ($this->minimumLength > 1) {
    foreach ($string as &$word)
    {
        if (strlen($word) < $this->minimumLength) {
            unset($word);
        }
    }
}

奇怪的是,这会使我的数组里一些单词长度不足。在检查了整个类代码后,我尝试了以下方法:

if ($this->minimumLength > 1) {
        foreach ($string as $key => $word)
        {
            if (strlen($word) < $this->minimumLength) {
                unset($string[$key]);
            }
        }
    }

并且!这个方法完美地运作了。那么,为什么会这样呢?我查看了PHP文档,它说:

如果一个以引用方式传递的变量在函数内部被unset(),那么只有本地变量会被销毁。调用环境中的变量将保留与调用unset()之前相同的值。

foreach在这里是否充当了调用环境,因为它有自己的作用域?


永远不要修改你正在迭代的东西,因为这可能会导致意想不到的行为。 - Waleed Khan
2
不回答关于参考文献的问题,但实现您想要的最简单和最干净的方法是使用array_filter()函数 - http://www.php.net/manual/en/function.array-filter.php - Mark Baker
谢谢,我之前使用过array_filter,但是用于不同的目的。我不会想到它可以用于从数组中删除元素这样“简单”的操作,但似乎将它们设置为false并在其上运行array_filter确实是最清晰的方法。 - user2742648
2个回答

2

这里没有函数调用,也没有传递变量的引用(在迭代过程中只是通过引用进行捕获)。

当您通过引用进行迭代时,迭代变量是原始变量的别名。当您使用此别名引用原始变量并修改其值时,更改将保留在被迭代的数组中可见。

然而,当您使用unset取消别名时,原始变量并不会“销毁”;别名只是从符号表中移除。

foreach ($string as $key => &$word)
{
    // This does not mean that the word is removed from $string
    unset($word);

    // It simply means that you cannot refer to the iteration variable using
    // $word from this point on. If you have captured the key then you can
    // still refer to it with $string[$key]; otherwise, you have lost all handles
    // to it for the remainder of the loop body
}

哦...不知怎么的,我一直以为所有针对值引用的操作都会反映到值本身上。看来我错了,谢谢你澄清了这一点。 - user2742648
1
@igorpan:一个相关的技巧是,当您使用引用进行迭代时,可能希望在循环体之后立即取消设置循环变量;否则,您将“冒着”分配给该变量的风险,这将覆盖迭代的上一个值。 - Jon
我知道这个,循环变量即使在循环结束后仍然保持定义。尽管如此,如果有人遇到这个问题,它可能会很有用 :) - user2742648

1

当你在if语句中调用unset($word)时,你删除的是$word变量本身,没有对$string数组做任何更改。


但为什么当我通过引用传递时会这样呢?我的foreach语句是foreach ($string as &$word) - user2742648
因为它是对原始变量的引用,而不是实际的原始变量;你正在取消引用,但原始变量仍然存在。 - Mark Baker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接