【问题标题】:Yield from closure关闭收益
【发布时间】:2018-08-15 12:23:28
【问题描述】:

我想提供一些数据。问题是 chunk 方法需要关闭才能完成这项工作。在这种情况下,有没有办法从 foreach 循环中产生数据?

public function yieldRowData(): \Iterator
{
    $this->transactionQuery
        ->getQuery()
        ->chunk(5, function ($collection) { // This is executed for each 5 elements from database
            foreach ($collection as $element) {
                yield $element->getId(); // I want this to be yield from this method (yieldRowData)
            }
        });
}

我试过了,但它只会返回最后的结果:

public function yieldRowData(): \Iterator
{
    $results = [];

    $this->transactionQuery
        ->getQuery()
        ->chunk(5, function(Collection $transactions) use (&$results) {
            $results = $transactions;
        });

    foreach($results as $transactionEntity) {
        yield $transactionEntity;
    }
}

【问题讨论】:

  • 我做了,但这对我不起作用。你能提供一些代码吗?我发布了我实现这一点的尝试。
  • chunk 方法是否是某些第三方库/框架的一部分?我认为您需要的是该方法的某个版本,它本身返回一个迭代器,或者您明确要求下一个块的类似对象,而不是将其传递给回调。
  • 是的,这是图书馆。没有办法覆盖它。

标签: php php-7.2


【解决方案1】:

您没有明确提及chunk() 方法(或其他方法)是否从闭包内返回产生的数据;但我想不会。

如果它返回(即它是一个生成器函数),那么yield from 是最好的选择:

// We are inside the method
yield from $query->chunk(5, function ($collection) {
    foreach ($collection as $element) {
        yield $element->getId();
    }
});

但是,如果它不是生成器函数,那么您必须使用其他解决方案。正如其他人提到的那样,您可能不想将结果保存在变量中并返回它,因为它会浪费内存(尤其是在大数据大小时)。

但还有另一种可能有效的解决方案。如果幸运的话,chunk() 中的代码不会很复杂,因此您可以从它的类中扩展并将chunk() 方法重写为生成器。您可能无法做到这一点,但通常这是可能的,而且并不难或耗时。

【讨论】:

  • 哦,我的...您正在回答/质疑我 1 年前遇到的问题:D 我记得返回值不是生成器。一切都在库和指定的功能内完成。我确实为此找到了一些解决方案,但我什至无法再访问该项目来检查它是如何完成或省略的。
  • @imclickingmaniac 哦,对于一年的延迟感到抱歉。然而,其他人可能会因此而受益。
【解决方案2】:

试试这个,它应该更接近你的第一次尝试,同时实际产生一些明智的结果:

public function yieldRowData(): \Iterator
{
    $results = [];

    $this->transactionQuery
        ->getQuery()
        ->chunk(5, function(Collection $collection) use (&$results) {
            foreach ($collection as $element) {
                $results[] = $element->getId();
            }
        });

    foreach($results as $transactionEntity) {
        yield $transactionEntity;
    }
}

为以下评论添加替代想法: 将闭包传入 yieldRowData() 以使用块的内存效率:

public function workRowData(\closure $rowDataWorker)
{
    $this->transactionQuery
        ->getQuery()
        ->chunk(5, $rowDataWorker);
}

$rowDataWorker 可以绑定到应该包含一些最终结果的变量,或者可以使用$this 访问实例变量。

【讨论】:

  • ->getId() 只是一个例子。事实上,我需要更多的数据。您的示例将起作用,但是 chunk 没有任何意义,因为您在内存上构建了整个数据。在这种情况下yield 也是没用的,因为你已经在数组中设置了完整的数据,所以你可以返回它而不是屈服。这样的代码根本不会节省内存。谢谢。
  • 确实如此,您可以做的是传入对数据进行操作的闭包。 yieldRowData(\closure $rowDataWorker) 如果对你有帮助的话。
  • 您的第二个示例引入了包装器,但没有额外的行为;这是 OP 正在努力解决的 $rowDataWorker 的定义,而不是在哪里传递它。
  • OP 没有给出任何关于如何使用数据的指示,所以我只是给出了另一种方式来思考这个问题,因为原始问题无法按照要求的方式解决。 :)
  • @ChristianM 是的,我只是指出它根本没有真正重构问题,它只是给单个语句 $this->transactionQuery->getQuery()->chunk(5,$foo) 一个函数名称,因此您可以将其缩短为 @987654331 @。还不如叫function thisTransactionQueryChunk5($rowDataWorker);它周围的其余代码看起来完全相同,并且工作方式或不完全相同。
猜你喜欢
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-03
  • 1970-01-01
  • 2019-09-19
  • 2019-07-11
  • 2012-08-15
相关资源
最近更新 更多