PHP-эквивалент оператора вывода Python
В Python (и других) вы можете постепенно обрабатывать большие объемы данных, используя оператор "yield" в функции. Что было бы аналогичным способом сделать это в PHP?
Например, скажем, в Python, если бы я хотел прочитать потенциально очень большой файл, я мог бы работать над каждой строкой один раз за один раз (этот пример надуман, так как это в основном то же самое, что и для line в 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), я использую переменную частного экземпляра, чтобы отслеживать состояние и действовать соответственно каждый раз, когда вызывается функция, но похоже, что должен быть лучший способ.
Ответы
Ответ 1
PHP имеет прямой эквивалент, называемый generators.
Старый (pre php 5.5 ответ):
К сожалению, языковой эквивалент не существует. Самый простой способ - либо то, что вы уже делаете, либо создать объект, который использует переменные экземпляра для сохранения состояния.
Однако существует хорошая возможность, если вы хотите использовать эту функцию в сочетании с оператором foreach: Итераторы SPL. Их можно использовать для достижения чего-то, похожего на генераторы питона.
Ответ 2
В https://wiki.php.net/rfc/generators есть rfc, который может быть включен в 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";
Ответ 3
Я прототип всего в Python перед реализацией на любых других языках, включая PHP. Я закончил использовать обратные вызовы для достижения того, что я хотел бы с 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
передается функции обратного вызова, и вы можете делать то, что хотите.
Ответ 4
Расширение ответа @Luiz - еще один классный способ - использовать анонимные функции:
function iterator($n, $cb)
{
for($i=0; $i<$n; $i++) {
call_user_func($cb, $i);
}
}
$sum = 0;
iterator(10,
function($i) use (&$sum)
{
$sum += $i;
}
);
print $sum;
Ответ 5
Не может быть эквивалентного оператора, но следующий код эквивалентен функции и служебных данных:
function file_lines($file) {
static $fhandle;
if ( is_null($fhandle) ) {
$fhandle = fopen($file, 'r');
if ( $fhandle === false ) {
return false;
}
}
if ( ($line = fgets($fhandle))!== false ) {
return $line;
}
fclose($fhandle);
$fhandle = null;
}
while ( $line = file_lines('some_file') ) {
// ...
}
Это выглядит правильно. Извините, я его не тестировал.
Ответ 6
То же предложение 'yield' существует теперь на PHP 5.5:
http://php.net/manual/en/language.generators.syntax.php