Geradores, visão geral
(PHP 5 >= 5.5.0, PHP 7, PHP 8)
Os geradores fornecem uma maneira fácil de implementar
iteradores simples sem a
sobrecarga ou complexidade de criar uma classe que implemente
a interface Iterator.
Um gerador oferece uma maneira conveniente de fornecer dados a loops foreach
sem ter que construir um array na memória antecipadamente, o que pode fazer com que o programa
exceda um limite de memória ou exija uma quantidade considerável de
tempo de processamento para essa geração. Em vez disso, uma função geradora pode ser usada,
que é o mesmo que uma
função normal, exceto que
ao invés do
retorno ocorrer uma única vez, um
gerador pode executar yield quantas vezes forem necessárias para fornecer os
valores a serem iterados.
Tal como acontece com os iteradores, o acesso aleatório aos dados não é possível.
Um exemplo simples para isso é reimplementar a função
range(). A função range() padrão
tem que gerar um array com cada valor dentro dele e retorná-lo, o que pode
resultar em grandes arrays: por exemplo, chamar
range(0, 1000000) irá resultar numa utilização de memória
de mais de 100 MB.
Como alternativa, nós podemos implementar um gerador
xrange(), que só precisará de memória suficiente para
criar um objeto Iterator e acompanhar o estado atual
do gerador internamente, que utiliza menos de 1 kilobyte de memória.
Exemplo #1 Implementando range() como um gerador
<?php
function xrange($start, $limit, $step = 1) {
if ($start <= $limit) {
if ($step <= 0) {
throw new LogicException('O passo precisa ser positivo');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('O passo precisa ser negativo');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
/*
* Observe abaixo que ambos range() e xrange() tem a mesma
* saída.
*/
echo 'Números ímpares a partir de range(): ';
foreach (range(1, 9, 2) as $number) {
echo "$number ";
}
echo "\n";
echo 'Números ímpares a partir de xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
echo "$number ";
}
?>
O exemplo acima produzirá:
Números ímpares a partir de range(): 1 3 5 7 9
Números ímpares a partir de xrange(): 1 3 5 7 9
Quando uma função geradora é chamada, um objeto interno
de tipo Generator é retornado. Este objeto
implementa a interface Iterator que um objeto
somente leitura de uma direção implementaria, provendo também métodos que
podem ser utilizados para manipular o estado do gerador, incluindo enviar
e retornar valores.