介绍
自 PHP 5.5 版本以来,出现了以下很棒的功能发电机 http://php.net/manual/en/language.generators.overview.php。我不会重复官方手册页,但它们对于迭代器的简短定义来说是很棒的事情。最知名的样本是:
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 的内容,现在开始讨论我的问题。想象一下我们想要创建生成器斐波那契数列 http://en.wikipedia.org/wiki/Fibonacci_number。通常,要获得这些,我们可以使用简单的函数:
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
函数所以它会yield我的值,不将它们保存在数组中?我的$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
value.
Bonus:获取斐波那契序列只是更一般问题的示例:在常见情况下如何使用递归生成器?当然,我可以使用标准Iterator http://php.net/manual/en/class.iterator.php为此或重写我的函数以避免递归。但我想用发电机来实现这一点。这可能吗?这是否值得努力以这种方式使用?