【问题标题】:What is the difference between a generator and an array?生成器和数组有什么区别?
【发布时间】:2013-06-17 21:49:58
【问题描述】:

今天,PHP 团队发布了PHP 5.5.0 版本,其中包括对generators 的支持。阅读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";
}

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

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

【问题讨论】:

标签: php arrays iterator generator


【解决方案1】:

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

数组使用它包含的值,并且必须预先填充这些值;生成器可以根据特殊标准创建直接使用的值......例如斐波那契序列,或非 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, ' ';
}

【讨论】:

    【解决方案2】:

    生成器允许对复杂语句进行惰性求值。这样可以节省内存,因为您不必一次分配所有内容。

    除了都是可迭代的之外,它们几乎不一样。 array 是数据结构,生成器不是。

    【讨论】:

      【解决方案3】:

      区别在于效率。例如,除 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 关于这个确切的主题。

      【讨论】:

        【解决方案4】:

        在 Python 中:

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

        ...生成器一次执行一个 yield 语句,在两者之间暂停以将执行返回到主 for 循环。

        -learnpython.org

        【讨论】:

          猜你喜欢
          • 2011-08-26
          • 2010-11-04
          • 2019-10-25
          • 2018-07-08
          • 2014-04-08
          • 2023-04-10
          • 2011-03-05
          • 2014-05-02
          • 1970-01-01
          相关资源
          最近更新 更多