【发布时间】:2013-11-19 01:44:13
【问题描述】:
简介
自 PHP 5.5 版本以来,就有了 generators 这样的好东西。我不会重复官方手册页,但它们对于迭代器的简短定义非常有用。最知名的样本是:
function xrange($from, $till, $step)
{
if ($from>$till || $step<=0)
{
throw new InvalidArgumentException('Invalid range initializers');
}
for ($i = $from; $i < $till; $i += $step)
{
yield $i;
}
}
//...
foreach (xrange(2, 13, 3) as $i)
{
echo($i.PHP_EOL); // 2,5,8,11
}
而生成器实际上不是一个函数,而是一个具体类的实例:
get_class(xrange(1, 10, 1)); // Generator
问题
完成了 RTM 的内容,现在继续我的问题。假设我们要创建Fibonacci numbers 的生成器。通常,要获得这些,我们可以使用简单的函数:
function fibonacci($n)
{
if(!is_int($n) || $n<0)
{
throw new InvalidArgumentException('Invalid sequence limit');
}
return $n < 2 ? $n : fibonacci($n-1) + fibonacci($n-2);
}
var_dump(fibonacci(6)); // 8
让我们把它转换成某种东西,它包含 sequence 而不仅仅是它的最后一个成员:
function fibonacci($n)
{
if (!is_int($n) || $n<0)
{
throw new InvalidArgumentException('Invalid sequence limit');
}
if ($n<2)
{
return range(0, $n);
}
$n1 = fibonacci($n-1);
$n2 = fibonacci($n-2);
return array_merge($n1, [array_pop($n1)+array_pop($n2)]);
}
//...
foreach (fibonacci(6) as $i)
{
echo($i.PHP_EOL); // 0,1,1,2,3,5,8
}
我们现在有一个返回完整序列数组的函数
问题
最后,问题部分:如何转换我最新的fibonacci 函数,使其产生我的值,而不是将它们保存在数组中?我的$n 可能很大,所以我想利用生成器的好处,比如xrange 示例。伪代码将是:
function fibonacci($n)
{
if (!is_int($n) || $n<0)
{
throw new InvalidArgumentException('Invalid sequence limit');
}
if ($n<2)
{
yield $n;
}
yield fibonacci($n-2) + fibonacci($n-1);
}
但这显然是废话,因为我们不能这样处理它,因为递归将导致类 Generator 的对象而不是 int 值。
奖励:获取斐波那契数列只是更一般问题的一个示例:在常见情况下如何使用具有递归的生成器?当然,我可以使用标准的Iterator 或者重写我的函数以避免递归。但我想用生成器来实现。这可能吗?这值得努力使用这种方式吗?
【问题讨论】:
-
好问题。我真的很期待看到答案
-
当你调用一个生成器时,你得到的是一个...生成器对象。该对象保存 state 并在被要求时返回和推进该状态。递归函数 OTOH 接受一个值并返回一个值。我看不出这两者如何相互兼容。等待进一步的意见。
-
@deceze 这也是我现在的想法。 IE。如果有足够的证据,“没办法做到这一点”也是很好的答案。
-
我猜你可以通过足够的内省和多态行为来做到这一点,但你为什么要这样做呢?我认为很难证明它不可能,但很容易证明以非递归方式编写它要简单得多。 (证明:你甚至不知道从哪里开始。;))
-
.. 这也是问题的一部分:p即如果“这样做不值得”——那么我很高兴看到答案——为什么。我现在不知道,是的:/我真丢脸。我通常的 SO 问题总是有我自己的解决方案(即使它不是很好)