关于数组:使用PHP迭代器实现与’array_walk_recursive()’函数相同的功能的正确方法是什么?

What is the proper way to use PHP iterators to implement identical functionality to the 'array_walk_recursive()' function?

我需要对所有数组元素进行递归迭代(即,对于遇到的本身就是数组的每个数组元素,我都需要迭代该数组)并在每个字符串(叶)值上执行trim()。 >

要使用与下面的链接中所述非常相似的示例,这是我需要的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php


    // How to accomplish identical functionality using PHP iterators?


    $sweet = array('a' => ' apple ', 'b' => ' banana ');
    $fruits = array('sweet' => $sweet, 'sour' => ' lemon ');

    function my_trim(&$item, $key)
    {
        // Trim the whitespace from the values
        $item = trim($item);
    }

    array_walk_recursive($fruits, 'my_trim');

?>

array_walk_recursive函数完全提供了我需要的功能。

但是,我想更好地理解PHP迭代器,并且通过使用迭代器实现与array_walk_recursive()函数相同的功能,我当前的任务似乎是一个很好的机会。

当我进入有关迭代器的PHP文档时,我看到了各种迭代器,但没有对其语义的整体描述。特别是,RecursiveArrayIterator似乎是显而易见的选择,如果不是针对RecursiveIteratorIterator类,并且此StackOverflow回答了类似的问题(它给出了代码,但没有使我满意地解释代码),我将尝试使用以前的迭代器(RecursiveArrayIterator),我什至可能都没有发布此问题。

但是,为了实际理解两个相关的迭代器类(RecursiveArrayIteratorRecursiveIteratorIterator)的文档,以便理解上面链接中的代码并满意使用(或不使用)-我在Google搜索中的任何地方都找不到关于这些迭代器语义的文档(特别是,如上所述,在官方的PHP文档中,我没有看到关于这些迭代器中任何一个的入门文档)。

因此,我想知道使用PHP迭代器实现与array_walk_recursive()函数相同的功能的正确方法是什么。 (大概,如果上面的SO链接上的代码正确,那么它将与链接的代码完全一样(或与之相似)-但我不仅要提供一个代码段,而且还要一个(简短的)说明。)


迭代器无意修改其内部元素。 Iterator用于在foreach循环中一个接一个地访问元素集合。

当然,您可以以返回修改后的字符串版本的方式覆盖Iterator的current()函数。但是,您仍然需要自己遍历所有元素以对其进行修改。

我将尝试通过示例进行说明:

1
2
$sweet = array('a' => ' apple ', 'b' => ' banana ');
$fruits = array('sweet' => $sweet, 'sour' => ' lemon ');

创建自定义的RecursiveIteratorIterator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CallbackRecursiveIteratorIterator extends
    RecursiveIteratorIterator
{

    public function __construct(RecursiveIterator $it, $callback) {
        $this->callback = $callback;
        parent::__construct($it);
    }  

    public function current() {
        return $this->callback->__invoke(parent::current());
    }  

}

创建该迭代器的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$i = new CallbackRecursiveIteratorIterator (
    new RecursiveArrayIterator(
            $fruits
    ),  
    // pass callback here
    function($element) {
        return trim($element ." test");
    }  
);


// Iterate over modified values
foreach($i as $key => $el) {
    var_dump($el);
}

请注意,原始数组仍未更改:

1
var_dump($fruits);

我不知道使用RecursiveIteratorIterator修改原始数组的简单方法,相反,如您的示例所用的array_walk_recursive是更好的选择。

如果只想访问不修改原始数组的多维数组的叶子,则可以使用LEAVES_ONLY选项。

1
2
3
4
5
6
7
8
9
$sweet = array('a' => ' apple ', 'b' => ' banana ');
$fruits = array('sweet' => $sweet, 'sour' => ' lemon ');

foreach (new RecursiveIteratorIterator(
             new RecursiveArrayIterator($fruits),
             RecursiveIteratorIterator::LEAVES_ONLY
         ) as $key => $value) {
    print trim($value);
}

请注意,在上面的foreach中,引用$ value即as $key => &$value是非法的,在这种情况下php将产生以下错误

1
PHP Fatal error:  An iterator cannot be used with foreach by reference

RecursiveIteratorIterator递归地迭代实现Traversable(并扩展为Iterator)接口的任何递归对象。对于嵌套数组,您需要一个RecursiveArrayIterator迭代器对象
但是例如,您可能还具有RecursiveDirectoryIterator来遍历文件系统。

您遍历对象的路径可以由RecursiveIteratorIterator的第二个参数指定:

1
2
3
RecursiveIteratorIterator::LEAVES_ONLY - The default. Lists only leaves in iteration.
RecursiveIteratorIterator::SELF_FIRST - Lists leaves and parents in iteration with parents coming first.
RecursiveIteratorIterator::CHILD_FIRST - Lists leaves and parents in iteration with leaves coming first.