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.