¿Qué es yield
?
La yield
palabra clave devuelve datos de una función generadora:
El corazón de una función generadora es la palabra clave de rendimiento. En su forma más simple, una declaración de rendimiento se parece mucho a una declaración de retorno, excepto que en lugar de detener la ejecución de la función y regresar, el rendimiento proporciona un valor al código que se repite sobre el generador y detiene la ejecución de la función del generador.
¿Qué es una función generadora?
Una función de generador es efectivamente una forma más compacta y eficiente de escribir un iterador . Le permite definir una función (su xrange
) que calculará y devolverá valores mientras realiza un bucle sobre ella :
foreach (xrange(1, 10) as $key => $value) {
echo "$key => $value", PHP_EOL;
}
Esto crearía el siguiente resultado:
0 => 1
1 => 2
…
9 => 10
También puede controlar el $key
en el foreach
mediante
yield $someKey => $someValue;
En la función de generador, $someKey
es lo que desea que aparezca $key
y $someValue
es el valor en $val
. En el ejemplo de la pregunta, eso es $i
.
¿Cuál es la diferencia con las funciones normales?
Ahora puede preguntarse por qué no estamos simplemente usando la range
función nativa de PHP para lograr esa salida. Y tienes razón. La salida sería la misma. La diferencia es cómo llegamos allí.
Cuando usamos range
PHP, lo ejecutaremos, crearemos toda la matriz de números en la memoria y todareturn
esa matriz en el foreach
ciclo que luego lo recorrerá y generará los valores. En otras palabras, foreach
operará en la matriz misma. La range
función y la foreach
única "charla" una vez. Piense en ello como recibir un paquete por correo. El repartidor te entregará el paquete y se irá. Y luego desenvuelves todo el paquete, sacando lo que esté allí.
Cuando usamos la función de generador, PHP entrará en la función y la ejecutará hasta que cumpla con el final o una yield
palabra clave. Cuando se encuentra con a yield
, devolverá lo que sea el valor en ese momento al bucle externo. Luego vuelve a la función de generador y continúa desde donde cedió. Como tu xrange
mantiene un for
bucle, se ejecutará y cederá hasta que $max
se alcance. Piense en ello como el foreach
y el generador jugando ping pong.
¿Por qué necesito eso?
Obviamente, los generadores se pueden usar para evitar los límites de memoria. Dependiendo de su entorno, hacer un range(1, 1000000)
testamento fatal en su script, mientras que lo mismo con un generador funcionará bien. O como dice Wikipedia:
Debido a que los generadores calculan sus valores producidos solo bajo demanda, son útiles para representar secuencias que serían costosas o imposibles de calcular de una vez. Estos incluyen, por ejemplo, secuencias infinitas y secuencias de datos en vivo.
También se supone que los generadores son bastante rápidos. Pero tenga en cuenta que cuando hablamos de rápido, generalmente hablamos en números muy pequeños. Entonces, antes de salir corriendo y cambiar todo el código para usar generadores, haga un punto de referencia para ver dónde tiene sentido.
Otro caso de uso para generadores son las rutinas asíncronas. La yield
palabra clave no solo devuelve valores, sino que también los acepta. Para obtener detalles sobre esto, consulte las dos excelentes publicaciones de blog vinculadas a continuación.
¿Desde cuándo puedo usar yield
?
Se han introducido generadores en PHP 5.5 . Intentar usar yield
antes de esa versión generará varios errores de análisis, dependiendo del código que sigue a la palabra clave. Entonces, si obtiene un error de análisis de ese código, actualice su PHP.
Fuentes y lecturas adicionales:
yeild
, digamos, una solución como esta: ideone.com/xgqevMreturn range(1,100000000)
yfor ($i=0; $i<100000000; $i++) yield $i
Esta función está usando rendimiento:
es casi lo mismo que este sin:
La única diferencia es que
a()
devuelve un generador yb()
solo una matriz simple. Puedes iterar en ambos.Además, el primero no asigna una matriz completa y, por lo tanto, requiere menos memoria.
fuente
ejemplo simple
salida
ejemplo avanzado
salida
fuente
yield
La palabra clave sirve para la definición de "generadores" en PHP 5.5. Ok, entonces, ¿qué es un generador ?Desde php.net:
Desde este lugar: generadores = generadores, otras funciones (solo funciones simples) = funciones.
Por lo tanto, son útiles cuando:
necesitas hacer cosas simples (o cosas simples);
El generador es realmente mucho más simple que implementar la interfaz Iterator. Por otro lado, es de origen que los generadores son menos funcionales. compáralos .
necesita generar GRANDES cantidades de datos, ahorrando memoria;
En realidad, para ahorrar memoria, podemos generar los datos necesarios a través de funciones para cada iteración de bucle, y después de la iteración utilizar basura. así que aquí los puntos principales son: código claro y probablemente rendimiento. Vea qué es mejor para sus necesidades.
necesita generar una secuencia, que depende de valores intermedios;
Esto se extiende al pensamiento anterior. Los generadores pueden facilitar las cosas en comparación con las funciones. verifique el ejemplo de Fibonacci e intente hacer una secuencia sin generador. También los generadores pueden trabajar más rápido en este caso, al menos debido al almacenamiento de valores intermedios en variables locales;
Necesitas mejorar el rendimiento.
pueden trabajar más rápido que las funciones en algunos casos (ver beneficio anterior);
fuente
Con
yield
usted puede describir fácilmente los puntos de interrupción entre múltiples tareas en una sola función. Eso es todo, no tiene nada de especial.Si task1 y task2 están altamente relacionadas, pero necesita un punto de interrupción entre ellas para hacer otra cosa:
entonces los generadores son la mejor solución, porque no tiene que dividir su código en muchos cierres o mezclarlo con otro código, o usar devoluciones de llamada, etc. Simplemente use
yield
para agregar un punto de interrupción, y puede continuar desde allí. punto de interrupción si está listo.Agregar punto de interrupción sin generadores:
Agregar punto de interrupción con generadores
nota: es fácil cometer errores con los generadores, ¡así que siempre escriba pruebas unitarias antes de implementarlas! nota2: Usar generadores en un bucle infinito es como escribir un cierre que tiene una longitud infinita ...
fuente
Ninguna de las respuestas anteriores muestra un ejemplo concreto usando matrices masivas pobladas por miembros no numéricos. Aquí hay un ejemplo usando una matriz generada por
explode()
un archivo .txt grande (262MB en mi caso de uso):El resultado fue:
Ahora compare eso con un script similar, usando la
yield
palabra clave:El resultado de este script fue:
Claramente, el ahorro en el uso de memoria fue considerable (ΔMemoryUsage -----> ~ 270.5 MB en el primer ejemplo, ~ 450B en el segundo ejemplo).
fuente
Un aspecto interesante, que vale la pena discutir aquí, está cediendo por referencia . Cada vez que necesitamos cambiar un parámetro de modo que se refleje fuera de la función, tenemos que pasar este parámetro por referencia. Para aplicar esto a los generadores, simplemente anteponemos un ampersand
&
al nombre del generador y a la variable utilizada en la iteración:El ejemplo anterior muestra cómo cambiar los valores iterados dentro del
foreach
bucle cambia la$from
variable dentro del generador. Esto se debe a que$from
se obtiene por referencia debido al signo y antes del nombre del generador. Debido a eso, la$value
variable dentro delforeach
ciclo es una referencia a la$from
variable dentro de la función del generador.fuente
El siguiente código ilustra cómo el uso de un generador devuelve un resultado antes de la finalización, a diferencia del enfoque tradicional sin generador que devuelve una matriz completa después de la iteración completa. Con el siguiente generador, los valores se devuelven cuando están listos, no es necesario esperar a que se complete por completo una matriz:
fuente