php arrays iterator generator

php - ¿Cuál es la diferencia entre un generador y una matriz?



arrays iterator (4)

Hoy el equipo de PHP lanzó la versión PHP 5.5.0 , que incluye soporte para generadores . Al leer la documentación , noté que hace exactamente lo que podría hacer con una matriz.

Ejemplo de generador de equipo de PHP :

// Only PHP 5.5 function gen_one_to_three() { for ($i = 1; $i <= 3; $i++) { // Note that $i is preserved between yields. yield $i; } } $generator = gen_one_to_three(); foreach ($generator as $value) { echo "$value/n"; }

Resultado :

1 2 3

Pero puedo hacer lo mismo usando matrices. Y aún puedo seguir siendo compatible con las versiones anteriores de PHP.

Eche un vistazo :

// Compatible with 4.4.9! function gen_one_to_three() { $results = array(); for ($i = 1; $i <= 3; $i++) { $results[] = $i; } return $results; } $generator = gen_one_to_three(); foreach ($generator as $value) { echo "$value/n"; }

Entonces la pregunta es : ¿cuál es el propósito de la existencia de esta nueva característica? Tengo que reproducir todos los ejemplos de documentación sin usar la nueva función, reemplazándola con una matriz.

¿Alguien puede dar una buena explicación y tal vez un ejemplo que no es necesariamente imposible con versiones anteriores, pero el uso de generadores puede ayudar en el desarrollo?


Como en Python:

Cuando una iteración sobre un conjunto de elementos comienza a usar la instrucción for, se ejecuta el generador. Una vez que el código de función del generador alcanza una declaración de "rendimiento", el generador cede su ejecución al ciclo for, devolviendo un nuevo valor del conjunto. La función del generador puede generar tantos valores (posiblemente infinitos) como quiera, cediendo cada uno a su vez.

... Los generadores ejecutan las declaraciones de rendimiento de a una por vez, haciendo una pausa entre ellas para devolver la ejecución al ciclo for principal.

- learnpython.org


La diferencia está en términos de eficiencia. Por ejemplo, muchos idiomas distintos de PHP incluyen dos funciones de range() , range() y xrange() . Este es un buen ejemplo de generadores y por qué usarlos. Construyamos el nuestro:

function range($start, $end) { $array = array(); for ($i = $start; $i <= $end; $i++) { $array[] = $i; } return $array; }

Ahora que es realmente directo. Sin embargo, para rangos grandes, se necesita una GRAN cantidad de memoria. Si intentamos ejecutarlo con $start = 0 y $end = 100000000 , ¡probablemente nos quedemos sin memoria!

Pero si usamos un generador:

function xrange($start, $end) { for ($i = $start; $i <= $end; $i++) { yield $i; } }

Ahora usamos la memoria constante, pero todavía tenemos una "matriz" (como la estructura) que podemos iterar (y usar con otros iteradores) en el mismo espacio.

No reemplaza una matriz, pero proporciona una forma eficiente de evitar la necesidad de memoria ...

Pero también proporciona ahorros en términos de la generación de artículos. Como cada resultado se genera según sea necesario, puede retrasar la ejecución (recuperar o calcular) cada elemento hasta que lo necesite. Entonces, por ejemplo, si necesita obtener un elemento de una base de datos y realizar un procesamiento complejo alrededor de cada fila, puede retrasarlo con un generador hasta que realmente necesite esa fila:

function fetchFromDb($result) { while ($row = $result->fetchArray()) { $record = doSomeComplexProcessing($row); yield $record; } }

Entonces, si solo necesita los primeros 3 resultados, solo procesará los primeros tres registros.

Para obtener más información, escribí una publicación de blog sobre este tema exacto.


Los generadores permiten la evaluación perezosa de declaraciones complejas. De esta forma, ahorrará memoria ya que no tiene que asignar todo a la vez.

Además de ser iterables, no son casi lo mismo. Una array es una estructura de datos, un generador no lo es.


Una matriz debe contener cada valor sobre el que está pasando un bucle antes de comenzar el bucle; un generador crea cada valor "sobre la marcha" como se solicita, por lo que mucha menos memoria;

Una matriz funciona con los valores que contiene, y debe ser rellenada previamente con esos valores; un generador puede crear valores de acuerdo con criterios especiales para ser utilizado directamente ... por ejemplo, una secuencia fibonnaci, o letras de un alfabeto no AZ (calculado mediante el valor numérico UTF-8) que permita de manera efectiva alphaRange (''א'', ''ת'' );

EDITAR

function fibonacci($count) { $prev = 0; $current = 1; for ($i = 0; $i < $count; ++$i) { yield $prev; $next = $prev + $current; $prev = $current; $current = $next; } } foreach (fibonacci(48) as $i => $value) { echo $i , '' -> '' , $value, PHP_EOL; }

EDITAR

Solo por diversión, aquí hay un generador que devolverá el alfabeto hebreo como caracteres UTF-8

function hebrewAlphabet() { $utf8firstCharacter = 1488; $utf8lastCharacter = 1514; for ($character = $utf8firstCharacter; $character <= $utf8lastCharacter; ++$character) { yield html_entity_decode(''&#''.$character.'';'', ENT_NOQUOTES, ''UTF-8''); }; } foreach(hebrewAlphabet() as $character) { echo $character, '' ''; }