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.
Example #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
Objetos Generator
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.