O que é yield
?
A yield
palavra-chave retorna dados de uma função geradora:
O coração de uma função geradora é a palavra-chave yield. Em sua forma mais simples, uma declaração de rendimento se parece muito com uma declaração de retorno, exceto que, em vez de interromper a execução da função e retornar, o rendimento fornece um valor ao código que circula sobre o gerador e pausa a execução da função do gerador.
O que é uma função de gerador?
Uma função de gerador é efetivamente uma maneira mais compacta e eficiente de escrever um iterador . Ele permite que você defina uma função (sua xrange
) que calculará e retornará valores enquanto você estiver fazendo um loop sobre ela :
foreach (xrange(1, 10) as $key => $value) {
echo "$key => $value", PHP_EOL;
}
Isso criaria a seguinte saída:
0 => 1
1 => 2
…
9 => 10
Você também pode controlar o $key
no foreach
usando
yield $someKey => $someValue;
Na função do gerador, $someKey
é o que você deseja que apareça $key
e $someValue
seja o valor em $val
. No exemplo da pergunta é isso $i
.
Qual é a diferença para funções normais?
Agora você pode se perguntar por que não estamos simplesmente usando a range
função nativa do PHP para obter essa saída. E você está certo. A saída seria a mesma. A diferença é como chegamos lá.
Quando usamos range
PHP, irá executá-lo, criar toda a matriz de números na memória e return
que toda variedade ao foreach
circuito que irá, em seguida, passar por isso e fornecer os valores. Em outras palavras, o foreach
irá operar no próprio array. A range
função e a foreach
única "conversa" uma vez. Pense nisso como receber um pacote pelo correio. O entregador entregará o pacote e sairá. E então você desembrulha o pacote inteiro, retirando o que estiver lá.
Quando usamos a função de gerador, o PHP entra na função e a executa até encontrar o final ou uma yield
palavra - chave. Quando encontrar a yield
, retornará o valor naquele momento para o loop externo. Em seguida, ele volta para a função de gerador e continua a partir de onde produziu. Como você xrange
mantém um for
loop, ele será executado e renderá até que $max
seja atingido. Pense nisso como foreach
o gerador jogando pingue-pongue.
Por que eu preciso disso?
Obviamente, os geradores podem ser usados para solucionar os limites de memória. Dependendo do seu ambiente, executar um range(1, 1000000)
script fatal será um passo, enquanto o mesmo com um gerador funcionará bem. Ou como a Wikipedia coloca:
Como os geradores calculam seus valores gerados somente sob demanda, eles são úteis para representar sequências que seriam caras ou impossíveis de calcular de uma só vez. Isso inclui, por exemplo, sequências infinitas e fluxos de dados ao vivo.
Os geradores também devem ser bem rápidos. Mas lembre-se de que, quando estamos falando rápido, geralmente estamos falando em números muito pequenos. Portanto, antes que você corra e mude todo o seu código para usar geradores, faça uma referência para ver onde faz sentido.
Outro caso de uso para geradores são as rotinas assíncronas. A yield
palavra-chave não apenas retorna valores, mas também os aceita. Para detalhes sobre isso, veja as duas excelentes postagens no blog abaixo.
Desde quando posso usar yield
?
Geradores foram introduzidos no PHP 5.5 . Tentar usar yield
antes dessa versão resultará em vários erros de análise, dependendo do código que segue a palavra-chave. Portanto, se você receber um erro de análise desse código, atualize seu PHP.
Fontes e leituras adicionais:
yeild
mais de, digamos, uma solução como esta: ideone.com/xgqevMreturn range(1,100000000)
efor ($i=0; $i<100000000; $i++) yield $i
Esta função está usando yield:
é quase o mesmo que este sem:
A única diferença é que
a()
retorna um gerador eb()
apenas uma matriz simples. Você pode iterar em ambos.Além disso, o primeiro não aloca uma matriz completa e, portanto, requer menos memória.
fonte
exemplo simples
resultado
exemplo avançado
resultado
fonte
yield
A palavra-chave serve para definição de "geradores" no PHP 5.5. Ok, então o que é um gerador ?Do php.net:
Deste local: geradores = geradores, outras funções (apenas funções simples) = funções.
Portanto, eles são úteis quando:
você precisa fazer coisas simples (ou coisas simples);
O gerador é realmente muito mais simples do que implementar a interface Iterator. por outro lado, é claro que os geradores são menos funcionais. compare-os .
você precisa gerar grandes quantidades de memória para economizar dados;
na verdade, para economizar memória, podemos gerar os dados necessários por meio de funções para cada iteração de loop e, após a iteração, utilizar o lixo. então aqui os principais pontos são - código claro e provavelmente desempenho. veja o que é melhor para suas necessidades.
você precisa gerar sequência, que depende de valores intermediários;
isso está se estendendo ao pensamento anterior. geradores podem facilitar as coisas em comparação com funções. verifique o exemplo de Fibonacci e tente fazer a sequência sem gerador. Também os geradores podem trabalhar mais rápido neste caso, pelo menos por causa do armazenamento de valores intermediários em variáveis locais;
você precisa melhorar o desempenho.
eles podem trabalhar mais rápido que as funções em alguns casos (consulte o benefício anterior);
fonte
Com
yield
você, você pode descrever facilmente os pontos de interrupção entre várias tarefas em uma única função. Isso é tudo, não há nada de especial nisso.Se task1 e task2 estiverem altamente relacionadas, mas você precisará de um ponto de interrupção entre eles para fazer outra coisa:
então os geradores são a melhor solução, porque você não precisa dividir seu código em muitos fechamentos ou misturá-lo com outro código ou usar retornos de chamada, etc. Você apenas usa
yield
para adicionar um ponto de interrupção e pode continuar a partir disso. ponto de interrupção se você estiver pronto.Adicione ponto de interrupção sem geradores:
Adicionar ponto de interrupção com geradores
Nota: É fácil cometer erros nos geradores; portanto, sempre escreva testes de unidade antes de implementá-los! note2: Usar geradores em um loop infinito é como escrever um fechamento com comprimento infinito ...
fonte
Nenhuma das respostas acima mostra um exemplo concreto usando matrizes massivas preenchidas por membros não numéricos. Aqui está um exemplo usando uma matriz gerada por
explode()
um arquivo .txt grande (262MB no meu caso de uso):A saída foi:
Agora compare isso com um script semelhante, usando a
yield
palavra-chave:A saída para este script foi:
Claramente, a economia no uso da memória foi considerável (ΔMemoryUsage -----> ~ 270,5 MB no primeiro exemplo, ~ 450B no segundo exemplo).
fonte
Um aspecto interessante, que vale a pena discutir aqui, é cedido por referência . Toda vez que precisamos alterar um parâmetro para que seja refletido fora da função, precisamos passar esse parâmetro por referência. Para aplicar isso aos geradores, simplesmente adicionamos um e comercial
&
ao nome do gerador e à variável usada na iteração:O exemplo acima mostra como a alteração dos valores iterados no
foreach
loop altera a$from
variável dentro do gerador. Isso ocorre porque$from
é produzido por referência devido ao e comercial antes do nome do gerador. Por esse motivo, a$value
variável dentro doforeach
loop é uma referência à$from
variável dentro da função do gerador.fonte
O código abaixo ilustra como o uso de um gerador retorna um resultado antes da conclusão, ao contrário da abordagem tradicional de não gerador que retorna uma matriz completa após a iteração completa. Com o gerador abaixo, os valores são retornados quando prontos, sem a necessidade de esperar que uma matriz seja completamente preenchida:
fonte