PHP中与Python的yield运算符等效的实现
在Python(还有其他语言)中,你可以通过在函数里使用'yield'这个操作符,逐步处理大量数据。那么在PHP中,有什么类似的方式呢?
举个例子,假设我想在Python中读取一个可能非常大的文件,我可以像这样逐行处理每一行(这个例子有点简单,其实就是'for line in file_obj'的意思):
def file_lines(fname):
f = open(fname)
for line in f:
yield line
f.close()
for line in file_lines('somefile'):
#process the line
我现在在PHP中做的是,使用一个私有的实例变量来跟踪状态,每次调用函数时根据这个状态来执行,但我觉得应该有更好的方法。
6 个回答
11
我在用其他语言(比如PHP)实现之前,都会先用Python做个原型。最后我用回调函数来实现我想用的yield
功能。
function doSomething($callback)
{
foreach ($something as $someOtherThing) {
// do some computations that generates $data
call_user_func($callback, $data);
}
}
function myCallback($input)
{
// save $input to DB
// log
// send through a webservice
// etc.
var_dump($input);
}
doSomething('myCallback');
这样每个$data
都会传递给回调函数,你就可以随意处理它。
18
有一个关于这个问题的提案,可以在这个链接找到:https://wiki.php.net/rfc/generators,可能会在PHP 5.5中被加入。
与此同时,可以看看这个简单的“生成器函数”示例,它是在用户空间实现的。
namespace Functional;
error_reporting(E_ALL|E_STRICT);
const BEFORE = 1;
const NEXT = 2;
const AFTER = 3;
const FORWARD = 4;
const YIELD = 5;
class Generator implements \Iterator {
private $funcs;
private $args;
private $key;
private $result;
public function __construct(array $funcs, array $args) {
$this->funcs = $funcs;
$this->args = $args;
}
public function rewind() {
$this->key = -1;
$this->result = call_user_func_array($this->funcs[BEFORE],
$this->args);
$this->next();
}
public function valid() {
return $this->result[YIELD] !== false;
}
public function current() {
return $this->result[YIELD];
}
public function key() {
return $this->key;
}
public function next() {
$this->result = call_user_func($this->funcs[NEXT],
$this->result[FORWARD]);
if ($this->result[YIELD] === false) {
call_user_func($this->funcs[AFTER], $this->result[FORWARD]);
}
++$this->key;
}
}
function generator($funcs, $args) {
return new Generator($funcs, $args);
}
/**
* A generator function that lazily yields each line in a file.
*/
function get_lines_from_file($file_name) {
$funcs = array(
BEFORE => function($file_name) { return array(FORWARD => fopen($file_name, 'r')); },
NEXT => function($fh) { return array(FORWARD => $fh, YIELD => fgets($fh)); },
AFTER => function($fh) { fclose($fh); },
);
return generator($funcs, array($file_name));
}
// Output content of this file with padded linenumbers.
foreach (get_lines_from_file(__FILE__) as $k => $v) {
echo str_pad($k, 8), $v;
}
echo "\n";