生成器和数组有什么区别?

时间:2013-06-20 21:08:16

标签: php arrays iterator generator

今天,PHP团队发布了PHP 5.5.0版本,其中包括对生成器的支持。阅读the documentation,我注意到它完全可以用数组做。

PHP团队生成器示例

// Only PHP 5.5
function gen_one_to_three() {
    for ($i = 1; $i <= 3; $i++) {
        // Note that $i is preserved between yields.
        yield $i;
    }
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}

结果

1
2
3

但我可以使用数组做同样的事情。我仍然可以与早期版本的PHP保持兼容。

看一看

// Compatible with 4.4.9!
function gen_one_to_three() {
    $results = array();
    for ($i = 1; $i <= 3; $i++) {
        $results[] = $i;
    }

    return $results;
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}

所以问题是:这个新功能的存在目的是什么?我必须在不使用新功能的情况下播放所有文档示例,将其替换为数组。

任何人都可以给出一个很好的解释,也许是旧版本不一定不可能的例子,但使用生成器可以帮助开发吗?

4 个答案:

答案 0 :(得分:57)

不同之处在于效率。例如,PHP以外的许多语言包括两个range函数,range()xrange()。这是生成器的一个很好的例子,以及为什么要使用它们。让我们建立自己的:

function range($start, $end) {
    $array = array();
    for ($i = $start; $i <= $end; $i++) {
        $array[] = $i;
    }
    return $array;
}

现在真的很直接。但是,对于大范围,它需要大量的内存。如果我们尝试使用$start = 0$end = 100000000运行它,我们可能会耗尽内存!

但是如果我们使用了发电机:

function xrange($start, $end) {
    for ($i = $start; $i <= $end; $i++) {
        yield $i;
    }
}

现在我们使用常量内存,但仍然有一个“数组”(比如结构),我们可以在同一个空间中迭代(并与其他迭代器一起使用)。

它不会替换一个数组,但它确实提供了避免需要内存的有效方法......

但它也可以节省物品的生成。由于每个结果都是根据需要生成的,因此您可以延迟执行(获取或计算)每个元素,直到您需要它为止。因此,例如,如果您需要从数据库中获取项目并对每行执行一些复杂的处理,您可以使用生成器延迟它,直到您确实需要该行为止:

function fetchFromDb($result) {
    while ($row = $result->fetchArray()) {
        $record = doSomeComplexProcessing($row);
        yield $record;
    }
}

因此,如果您只需要前3个结果,则只需处理前三个记录。

有关详细信息,我就此确切主题撰写了blog post

答案 1 :(得分:13)

生成器允许复杂语句的延迟评估。这样就可以节省内存,因为您不必一次分配所有内容。

除了可迭代之外,它们几乎不相同。 array是数据结构,生成器不是。

答案 2 :(得分:11)

数组必须包含在开始循环之前循环的每个值;生成器根据请求“动态”创建每个值,因此内存少得多;

数组使用它包含的值,并且必须预先填充这些值;生成器可以根据要直接使用的特殊标准创建值...例如fibonnaci序列,或来自非A-Z字母表的字母(由UTF-8数值计算)有效地允许alphaRange('א','ת');

修改

function fibonacci($count) {
    $prev = 0;
    $current = 1;

    for ($i = 0; $i < $count; ++$i) {
        yield $prev;
        $next = $prev + $current;
        $prev = $current;
        $current = $next;
    }
}

foreach (fibonacci(48) as $i => $value) {
    echo $i , ' -> ' , $value, PHP_EOL;
}

修改

只是为了好玩,这是一个将希伯来字母作为UTF-8字符返回的生成器

function hebrewAlphabet() {
    $utf8firstCharacter = 1488;
    $utf8lastCharacter = 1514;
    for ($character = $utf8firstCharacter; $character <= $utf8lastCharacter; ++$character) {
        yield html_entity_decode('&#'.$character.';', ENT_NOQUOTES, 'UTF-8');
    };
}

foreach(hebrewAlphabet() as $character) {
    echo $character, ' ';
}

答案 3 :(得分:2)

与Python一样:

当使用for语句开始对一组项目的迭代时,将运行生成器。一旦生成器的函数代码达到“yield”语句,生成器就会将其执行返回到for循环,从集合中返回一个新值。生成器函数可以生成任意数量的值(可能是无限的),从而产生每个值。

...生成器一次执行一个yield语句,暂停之间以使执行返回到main for循环。

- learnpython.org