Я только что написал часть генератора и достиг точки, где он работает правильно, но нуждается в некотором рефакторинге. Структура метода следующая:
public function getFlattenedList( array $elements ) {
foreach ( $elements as $element ) {
if ( $this->someCondition( $element ) ) {
// A pile of stuff here
if ( $anotherCondition ) {
for ( $i = 0; $i < $this->someValue(); $i++ ) {
yield $this->anotherOperation( $element );
}
}
}
else {
yield $this->someOperation( $element );
}
}
}
Этот метод является большим / сложным. Самое очевидное, что нужно сделать, это переместить тело ветви if в свой собственный метод. Что-то вроде этого
public function getFlattenedList( array $elements ) {
foreach ( $elements as $element ) {
if ( $this->someCondition( $element ) ) {
// ???
$this->getFlattenedElement( $element );
}
else {
yield $this->someOperation( $element );
}
}
}
private function getFlattenedElement() {
// A pile of stuff here
if ( $anotherCondition ) {
for ( $i = 0; $i < $this->someValue(); $i++ ) {
yield $this->anotherOperation( $element );
}
}
}
Но, конечно, я не могу просто вернуть результат этой новой функции, поскольку она также является генератором. (И я хочу, чтобы это был генератор, поэтому работа выполняется только тогда, когда запрашивается значение.) Что я сделал, чтобы сделать эту работу, так это добавление еще одного цикла внутри тела условия if:
public function getFlattenedList( array $elements ) {
foreach ( $elements as $element ) {
if ( $this->someCondition( $element ) ) {
foreach ( $this->getFlattenedElement( $element ) as $flattened ) {
yield $flattened;
}
}
else {
yield $this->someOperation( $element );
}
}
}
Можно ли как-то избежать необходимости добавлять этот цикл, сохраняя при этом поведение генератора и правильно разделяя метод? Я раньше не использовал генераторы, поэтому, возможно, упускаю что-то очевидное.
То, что вы на самом деле пытаетесь сделать, известно как делегирование генератора, и теперь это стало вопросом версии PHP. Только PHP версии 7 и выше поддерживает делегирование генератора, и это выглядит так:
function gen_y() {
yield 1;
yield 2;
}
function gen_x() {
yield from gen_y();
yield from gen_y();
}
Это даст вам именно тот эффект, который вы хотите получить, или «рекурсивный», или «вложенный», или «делегированный» вызов генератора. Но проблема сейчас в PHP7. Не многие производственные среды работают с PHP7. Для этого вам нужно написать другой генератор, который берет генератор, который выдает генераторы. Как конвертер.
Я не буду вставлять код сюда, но я недавно столкнулся с этой проблемой, которая вынудила меня написать пакет для этого в PHP5: hedronium / генератор-гнездо
При использовании этого пакета вы просто получаете генераторы и оборачиваете исходный вызов генератора методом, предоставляемым пакетом.
use Hedronium\GeneratorNest\GeneratorNest;
function gen_x() {
yield gen_y();
yield gen_y();
}
foreach (GeneratorNest::nested(gen_x()) as $x) {
// Your Code.
}
Я бы посоветовал вам взглянуть на источник, чтобы понять, что на самом деле происходит. Это только 28 строк.;)
Других решений пока нет …