En informática , un generador es una rutina que se puede usar para controlar el comportamiento iterativo de un bucle . Todos los generadores son también iteradores . [ 1 ] Un generador es muy similar a una función que devuelve un array , ya que un generador tiene parámetros , se puede llamar y genera una secuencia de valores. Sin embargo, en lugar de construir un array que contenga todos los valores y devolverlos todos a la vez, un generador produce los valores uno a uno, lo que requiere menos memoria y permite que quien lo llama comience a procesar los primeros valores de inmediato. En resumen, un generador se parece a una función pero se comporta como un iterador .
Los generadores pueden implementarse en términos de construcciones de flujo de control más expresivas , como corrutinas o continuaciones de primera clase . [ 2 ] Los generadores, también conocidos como semicorrutinas, [ 3 ] son un caso especial de (y más débiles que) las corrutinas, ya que siempre devuelven el control al llamador (al pasar un valor de vuelta), en lugar de especificar una corrutina a la que saltar; véase la comparación de corrutinas con generadores .
Usos
Los generadores se invocan normalmente dentro de bucles. [ 4 ] La primera vez que se alcanza una invocación de generador en un bucle, se crea un objeto iterador que encapsula el estado de la rutina del generador al inicio, con argumentos vinculados a los parámetros correspondientes . El cuerpo del generador se ejecuta entonces en el contexto de ese iterador hasta que se encuentra una acción especial de yield ; en ese momento, el valor proporcionado con la acción de yield se utiliza como valor de la expresión de invocación. La siguiente vez que se alcanza la misma invocación de generador en una iteración posterior, la ejecución del cuerpo del generador se reanuda después de la acción de yield , hasta que se encuentra otra acción de yield . Además de la acción de yield , la ejecución del cuerpo del generador también puede terminarse mediante una acción de finish , momento en el que se termina el bucle más interno que contiene la invocación del generador. En situaciones más complejas, un generador puede utilizarse manualmente fuera de un bucle para crear un iterador, que luego puede utilizarse de diversas maneras.
Dado que los generadores calculan sus valores de salida solo bajo demanda, son útiles para representar flujos de datos , como secuencias que serían costosas o imposibles de calcular de una sola vez. Esto incluye, por ejemplo, secuencias infinitas y flujos de datos en tiempo real.
Cuando se desea una evaluación inmediata (principalmente cuando la secuencia es finita, ya que de lo contrario la evaluación nunca terminará), se puede convertir a una lista o usar una construcción paralela que cree una lista en lugar de un generador. Por ejemplo, en Python un generador gse puede evaluar a una lista lmediante l = list(g), mientras que en F# la expresión de secuencia seq { ... }se evalúa de forma diferida (un generador o una secuencia) pero [ ... ]se evalúa de forma inmediata (una lista).
En presencia de generadores, las estructuras de bucle de un lenguaje, como `for` y `while`, se pueden reducir a una sola estructura de bucle `... end`. Todas las estructuras de bucle habituales se pueden simular fácilmente utilizando generadores adecuados. Por ejemplo, un bucle con rango como `for` for x = 1 to 10se puede implementar como una iteración a través de un generador, como en Python for x in range(1, 10). Además, `for` breakse puede implementar enviando `finish` al generador y luego usando `finish` continuedentro del bucle.
Lenguajes que proporcionan generadores
Los generadores aparecieron por primera vez en CLU (1975), [ 5 ] fueron una característica destacada en el lenguaje de manipulación de cadenas Icon (1977) y ahora están disponibles en Python (2001), [ 6 ] C# , [ 7 ] Ruby , PHP , [ 8 ] ECMAScript (a partir de ES6/ES2015 ) y otros lenguajes. En CLU y C#, los generadores se llaman iteradores , y en Ruby, enumeradores .
Ceceo
El estándar final de Common Lisp no proporciona generadores de forma nativa, pero existen varias implementaciones de bibliotecas, como SERIES documentada en CLtL2 o pygen .
CLU
Una instrucción yield se utiliza para implementar iteradores sobre abstracciones de datos definidas por el usuario. [ 9 ]
cadena_caracteres = iterar (s: cadena) produce (carácter); índice: int := 1; límite: int := string$size (s); mientras índice <= límite hacer generar (string$fetch(s, índice)); índice := índice + 1; fin; fin de los caracteres de la cadena; para c: carácter en cadena_caracteres(s) hacer ... fin; Icono
Cada expresión (incluidos los bucles) es un generador. El lenguaje cuenta con numerosos generadores integrados e incluso implementa parte de la semántica lógica mediante el mecanismo de generadores ( la disyunción lógica u "OR" se realiza de esta manera).
La impresión de cuadrados del 0 al 20 se puede lograr utilizando una corrutina escribiendo:
Sin embargo, la mayoría de las veces los generadores personalizados se implementan con la palabra clave "suspend", que funciona exactamente igual que la palabra clave "yield" en CLU.
do
C no tiene funciones generadoras como una construcción de lenguaje , pero, como son un subconjunto de corrutinas , es sencillo implementarlas usando cualquier marco que implemente corrutinas con pila, como libdill. [ 10 ] En plataformas POSIX, cuando el costo del cambio de contexto por iteración no es una preocupación, o se desea paralelismo completo en lugar de mera concurrencia , se puede implementar un marco de funciones generadoras muy simple usando pthreads y tuberías .
C++
C++11 permite aplicar bucles `foreach`begin a cualquier clase que proporcione las funciones `foreach` y `foreach` end. De este modo, es posible escribir clases tipo generador definiendo tanto los métodos iterables (` beginforeach` y `foreach` end) como los métodos iteradores ( `foreach` operator!=, ` operator++foreach` y operator*`foreach`) en la misma clase. Por ejemplo, es posible escribir el siguiente programa con una implementación básica de rango:
importar std ;clase Range { privado : int último ; público : explícito Range ( int fin ) : último { fin } {}clase Iterador { privado : int iterador ; público : iterador explícito ( int inicio ) : iterador { inicio } {}[[ nodiscard ]] int operator * () const noexcept { return iter ; }[[ nodiscard ]] Iterador & operador ++ () noexcept { ++ iter ; return * this ; }[[ nodiscard ]] bool operator != ( const Iterator & other ) const noexcept { return iter != other . iter ; } };[[ nodiscard ]] Iterador begin () const noexcept { return Iterador ( 0 ); // Iniciar iteración desde 0 }[[ nodiscard ]] Iterador fin () const noexcept { return Iterador ( último ); // Finaliza la iteración en 'último' } };int main () { for ( int i : Range ( 10 )) { std :: println ( "{}" , i ); } return 0 ; }Además, C++20 introdujo formalmente el soporte para corrutinas, [ 11 ] que se pueden usar para implementar generadores. [ 12 ] C++23 introdujo std::generator<Ref, V, Alloc>[ 13 ] en la biblioteca estándar, lo que facilitó mucho la implementación de generadores. Por ejemplo, un generador de rango básico se puede implementar como:
importar std ;usando std :: generator ;generador < int > rango ( int n ) noexcept { para ( int i = 0 ; i < n ; ++ i ) { co_yield i ; } }Se puede iterar usando bucles foreach :
importar std ;usando std :: generator ;generador < int > rango ( int n ) noexcept { para ( int i = 0 ; i < n ; ++ i ) { co_yield i ; } }int main () { for ( int i : range ( 10 )) { std :: println ( "{}" , i ); } return 0 ; }Una vez que un generador se agota, coincidirá con el objeto centinela std::default_sentinel. Intentar incrementar el generador más allá de su agotamiento provoca un comportamiento indefinido .
DO#
Un ejemplo de generador de C# 2.0 ( yielddisponible desde la versión 2.0 de C#): Ambos ejemplos utilizan genéricos, pero esto no es obligatorio. La palabra clave yield también ayuda a implementar iteraciones con estado personalizadas sobre una colección, como se explica en esta discusión. [ 14 ]
usando System.Collections.Generics ;// Método que toma una entrada iterable (posiblemente un array) // y devuelve todos los números pares. public static IEnumerable < int > GetEven ( IEnumerable < int > numbers ) { foreach ( int number in numbers ) { if (( number % 2 ) == 0 ) { yield return number ; } } }Es posible utilizar varias yield returninstrucciones, las cuales se aplican secuencialmente en cada iteración:
usando System.Collections.Generics ;public class CityCollection : IEnumerable < string > { public IEnumerator < string > GetEnumerator () { yield return "Nueva York" ; yield return "París" ; yield return "Londres" ; } }Perl
Perl no proporciona generadores de forma nativa, pero el módulo Coro::Generator ofrece soporte para ello, utilizando el marco de trabajo de corrutinas Coro . Ejemplo de uso:
use strict ; use warnings ; # Habilitar el generador { BLOCK } y yield use Coro::Generator ; # Referencia de matriz para iterar sobre my $chars = [ 'A' ... 'Z' ];# Nuevo generador que se puede llamar como una referencia de código. my $letters = generator { my $i = 0 ; for my $letter ( @$chars ) { # obtener la siguiente letra de $chars yield $letter ; } };# Llama al generador 15 veces. print $letters -> (), "\n" for ( 0 .. 15 );Raku
Un ejemplo similar a Icon utiliza la clase Range de Raku (anteriormente conocida como Perl 6) como una de las varias formas de lograr generadores con el lenguaje.
La impresión de cuadrados del 0 al 20 se puede lograr escribiendo:
para ( 0 .. *). map (* ** 2 ) -> $i { último si $i > 20 ; decir $i } Sin embargo, la mayoría de las veces, los generadores personalizados se implementan con las palabras clave "gather" y "take" en un contexto perezoso.
Tcl
En Tcl 8.6, el mecanismo generador se basa en corrutinas con nombre .
proc generator { body } { coroutine gen [ incr :: disambiguator ] apply {{ script } { # Produce el resultado de [generator], el nombre del generador yield [ info coroutine ] # Realiza la generación eval $script # Finaliza el bucle del llamador usando una excepción 'break' return - code break }} $body }# Usa un bucle 'for' simple para hacer el conteo real del conjunto de generación [ generador { para {establecer i 10 } { $i <= 20 } { incrementar i } { generar $i } }]# Extrae valores del generador hasta que se agote mientras 1 { puts [ $count ] }Haskell
En Haskell , con su modelo de evaluación perezosa , cada dato creado con un constructor de datos no estricto se genera bajo demanda. Por ejemplo,
countFrom :: Integer -> [ Integer ] countFrom n = n : countFrom ( n + 1 )de10a20 :: [ Entero ] de10a20 = tomarMientras ( <= 20 ) $ contarDesde 10primos :: [ Entero ] primos = 2 : 3 : siguientePrimo 5 donde siguientePrimo n | noDivisible n = n : siguientePrimo ( n + 2 ) | de lo contrario = siguientePrimo ( n + 2 ) noDivisible n = todos (( /= 0 ) . ( rem n )) $ tomarMientras (( <= n ) . ( ^ 2 )) $ cola primosdonde (:)es un constructor de lista no estricto, cons , y $es simplemente un operador "llamado con" , utilizado para la paréntesis. Esto utiliza la función adaptadora estándar,
takeWhile p [] = [] takeWhile p ( x : xs ) | p x = x : takeWhile p xs | otherwise = []que recorre la lista y se detiene en el primer elemento que no satisface el predicado. Si la lista ya se ha recorrido hasta ese punto, es simplemente una estructura de datos estricta , pero si alguna parte no se ha recorrido antes, se generará bajo demanda. Las comprensiones de listas se pueden usar libremente:
cuadradosMenoresDe20 = tomarMientras ( <= 20 ) [ x * x | x < - contarDesde10 ] cuadradosParaNúmerosMenoresDe20 = [ x * x | x <- tomarMientras ( < = 20 ) $ contarDesde10 ]Raqueta
Racket proporciona varias funcionalidades relacionadas con los generadores. En primer lugar, sus formas de bucle for funcionan con secuencias , que son una especie de productor:
( para ([ i ( en rango 10 20 )]) ( printf "i = ~s \n " i ))y estas secuencias también son valores de primera clase:
( define 10-to-20 ( in-range 10 20 )) ( for ([ i 10-to-20 ]) ( printf "i = ~s \n " i ))Algunas secuencias se implementan de forma imperativa (con variables de estado privadas) y otras como listas perezosas (posiblemente infinitas). Además, las nuevas definiciones de estructuras pueden tener una propiedad que especifica cómo se pueden usar como secuencias.
Pero más directamente, Racket viene con una biblioteca de generadores para una especificación de generador más tradicional. Por ejemplo,
#lang racket ( require racket/generator ) ( define ( ints-from from ) ( generator () ( for ([ i ( in-naturals from )]) ; secuencia infinita de enteros desde 0 ( yield i )))) ( define g ( ints-from 10 )) ( list ( g ) ( g ) ( g )) ; -> '(10 11 12)Cabe destacar que el núcleo de Racket implementa potentes funciones de continuación, proporcionando continuaciones generales (reentrantes) que son componibles, así como continuaciones delimitadas. La biblioteca de generadores se implementa en Racket utilizando estas funcionalidades.
PHP

La comunidad de PHP implementó generadores en PHP 5.5. Los detalles se pueden encontrar en la Solicitud de comentarios original: Generadores .
Secuencia infinita de Fibonacci:
función fibonacci () : Generador { $último = 0 ; $actual = 1 ; generar 1 ; mientras ( verdadero ) { $actual = $último + $actual ; $último = $actual - $último ; generar $actual ; } }foreach ( fibonacci () as $number ) { echo $number , " \n " ; }Secuencia de Fibonacci con límite:
función fibonacci ( int $limit ) : Generador { yield $a = $b = $i = 1 ;mientras ( ++ $i < $limit ) { yield $a = ( $b = $a + $b ) - $a ; } }foreach ( fibonacci ( 10 ) as $number ) { echo " $number \n " ; }Cualquier función que contenga una instrucción yield es automáticamente una función generadora.
Rubí
Ruby admite generadores (a partir de la versión 1.9) en forma de la clase Enumerator integrada.
# Generador a partir de un objeto Enumerator chars = Enumerator . new ( [ 'A' , 'B' , 'C' , 'Z' ] )4. veces { pone caracteres.siguiente }# Generador a partir de un contador de bloques = Enumerador.new do | yielder | i = 0 loop { yielder.yield i + = 1 } end100 veces { pone count.next }Java
Java ha contado con una interfaz estándar para implementar iteradores desde sus inicios, y desde Java 5, la construcción "foreach" facilita la iteración sobre objetos que proporcionan dicha java.lang.Iterableinterfaz. (El framework de colecciones de Java y otros frameworks de colecciones suelen proporcionar iteradores para todas las colecciones).
import java.util.stream.Stream ;registrar Par ( int a , int b ) {};Iterable < Integer > myIterable = Stream . iterate ( new Pair ( 1 , 1 ), p -> new Pair ( p . b , p . a + p . b )) . limit ( 10 ) . map ( p -> p . a ):: iterator ;miIterable.forEach ( System.out :: println ) ;O bien, obtener una java.util.Iteratorde la superinterfaz java.util.stream.BaseStreamde java.util.stream.Streaminterfaz de Java 8.
import java.util.Iterator ; import java.util.stream.Stream ;registrar Par ( int a , int b ) {};// Guarda el iterador de un flujo que genera la secuencia de Fibonacci // Stream::iterate genera la secuencia de Fibonacci Iterator < Integer > myGenerator = Stream . iterate ( new Pair ( 1 , 1 ), p -> new Pair ( p . b , p . a + p . b )) . map ( p -> p . a ). iterator ();// Imprime los primeros 5 elementos for ( int i = 0 ; i < 5 ; i ++ ) { System . out . println ( myGenerator . next ()); }System.out.println ( "Primera iteración terminada " ) ;// Imprime los siguientes 5 elementos for ( int i = 0 ; i < 5 ; i ++ ) { System . out . println ( myGenerator . next ()); }Producción:
1 1 2 3 5 completado con la primera iteración 8 13 21 34 55SG
En XL , los iteradores son la base de los bucles 'for':
import IO = XL.UI.CONSOLE iterador IntegerIterator (var out Counter : integer; Low, High : integer) escrito Counter en Low..High es Contador := Bajo bucle mientras el contador sea menor o igual a un valor alto producir Contador += 1 // Tenga en cuenta que no es necesario declarar I, ya que se declaró 'var out' en el iterador. // Por lo tanto, aquí se realiza una declaración implícita de I como un entero. para I en bucle 1..5 IO.WriteLn "Yo=", yo F#
F# proporciona generadores a través de expresiones de secuencia desde la versión 1.9.1. [ 15 ] Estas pueden definir una secuencia (evaluación diferida, acceso secuencial ) a través de seq { ... }, una lista (evaluación inmediata, acceso secuencial) a través de [ ... ]o una matriz (evaluación inmediata, acceso indexado) a través [| ... |]de que contienen código que genera valores. Por ejemplo,
secuencia { para b en 0 .. 25 hacer si b < 15 entonces generar b * b }forma una secuencia de cuadrados de números del 0 al 14 filtrando los números del rango del 0 al 25.
Pitón
Los generadores se añadieron a Python en la versión 2.2 en 2001. [ 6 ] Un ejemplo de generador:
import itertools from typing import Iteratordef countfrom ( n : int ) -> Iterator [ int ]: while True : yield n n += 1# Ejemplo de uso: imprimir los números enteros del 10 al 20. # Tenga en cuenta que esta iteración termina normalmente, a pesar de que # countfrom() está escrito como un bucle infinito.para i en countfrom ( 10 ): si i <= 20 : imprimir ( i ) sino : break# Otro generador que produce números primos indefinidamente según sea necesario.def primes () -> Iterator [ int ]: """Genera números primos indefinidamente según sea necesario.""" yield 2 n : int = 3 p : list [ int ] = [ 2 ] while True : # Si al dividir n por todos los números en p, hasta sqrt(n) inclusive, # se obtiene un resto distinto de cero, entonces n es primo. if all ( n % f > 0 for f in itertools . takewhile ( lambda f : f * f <= n , p )): yield n p . append ( n ) n += 2En Python, un generador puede considerarse como un iterador que contiene un marco de pila congelado . Cada vez next()que se llama a un iterador, Python reanuda el marco congelado, que se ejecuta normalmente hasta que yieldse encuentra la siguiente instrucción. El marco del generador se congela de nuevo y el valor generado se devuelve a quien lo llamó.
PEP 380 (implementado en Python 3.3) agrega la yield fromexpresión, permitiendo que un generador delegue parte de sus operaciones a otro generador o iterable. [ 16 ]
Expresiones generadoras
Python tiene una sintaxis basada en la de las comprensiones de listas , llamada expresión generadora, que facilita la creación de generadores. El siguiente ejemplo amplía el anterior utilizando una expresión generadora para calcular cuadrados a partir de la itertools.count()función generadora:
desde typing import Generatorcuadrados : Generador [ int , Ninguno , Ninguno ] = ( n * n para n en itertools.count ( 2 ) )para j en cuadrados : si j <= 20 : imprimir ( j ) sino : salirECMAScript
ECMAScript 6 (también conocido como Harmony) introdujo las funciones generadoras.
Se puede escribir una secuencia de Fibonacci infinita utilizando un generador de funciones:
función * fibonacci ( límite ) { sea [ prev , curr ] = [ 0 , 1 ]; mientras ( ! límite || curr <= límite ) { generar curr ; [ prev , curr ] = [ curr , prev + curr ]; } }// limitado por el límite superior 10 para ( const n de fibonacci ( 10 )) { console.log ( n ) ; }// generador sin límite superior for ( const n of fibonacci ()) { console . log ( n ); if ( n > 10000 ) break ; }// iterando manualmente let fibGen = fibonacci (); console.log ( fibGen.next ( ) . value ) ; // 1 console.log ( fibGen.next ( ) . value ); // 1 console.log ( fibGen.next ( ) . value ) ; // 2 console.log ( fibGen.next ( ) . value ) ; // 3 console.log ( fibGen.next ( ) . value ) ; // 5 console.log ( fibGen.next ( ) . value ) ; // 8// retoma desde donde lo dejaste for ( const n of fibGen ) { console . log ( n ); if ( n > 10000 ) break ; }R
El paquete iteradores puede utilizarse para este propósito. [ 17 ] [ 18 ]
biblioteca ( iteradores )# Ejemplo ------------------ abc <- iter ( c ( 'a' , 'b' , 'c' )) nextElem ( abc )Charla informal
Ejemplo en Pharo Smalltalk :
El generador de la proporción áurea que se muestra a continuación devuelve, con cada llamada a 'goldenRatio next', una mejor aproximación a la proporción áurea.
goldenRatio := Generador en: [ : g | | xyzr | x := 0 . y := 1 . [ z := x + y . r := ( z / y ) comoFloat . x := y . y := z . g produce: r ] repetir ] .Proporción áurea siguiente .La siguiente expresión devuelve las siguientes 10 aproximaciones.
Personaje cr unir: (( 1 a: 10 ) recolectar: [ : ficticio | proporción siguiente ]) .Ver más en Una joya oculta en Pharo: Generator .
Véase también
- Comprensión de lista para otra construcción que genera una secuencia de valores.
- Iterador para el concepto de generar una lista elemento por elemento.
- Iterar para una alternativa
- Evaluación perezosa para producir valores cuando sea necesario
- Corecursión para datos potencialmente infinitos mediante recursión en lugar de yield
- Corrutina para una mayor generalización a partir de subrutinas.
- Continuación para la generalización del flujo de control
Notas
- ↑ ¿Cuál es la diferencia entre un iterador y un generador?
- ↑ Kiselyov, Oleg (enero de 2004). "Formas generales de recorrer colecciones en Scheme" .
- ↑ Anthony Ralston (2000). Enciclopedia de la informática . Nature Pub. Group. ISBN 978-1-56159-248-7. Consultado el 11 de mayo de 2013 .
- ↑ El lenguaje de programación Icon utiliza generadores para implementar su evaluación orientada a objetivos. En Icon, los generadores pueden invocarse en contextos ajenos a las estructuras de control de bucle habituales.
- ↑ Liskov, Barbara (abril de 1992). "Una historia de CLU" (PDF) . Archivado del original (PDF) el 17 de septiembre de 2003. Recuperado el 5 de enero de 2006 .
- 1 2 Propuestas de mejora de Python: PEP 255: Generadores simples , PEP 289: Expresiones generadoras , PEP 342: Corrutinas mediante generadores mejorados
- ↑ rendimiento (Referencia de C#)
- ↑ "PHP: Descripción general de los generadores - Manual" .
- ↑ Liskov, B.; Snyder, A.; Atkinson, R.; Schaffert, C. (1977). "Mecanismos de abstracción en CLU". Communications of the ACM . 20 (8): 564– 576. CiteSeerX 10.1.1.112.656 . doi : 10.1145/359763.359789 . S2CID 17343380 .
- ↑ "Concurrencia estructurada para C" .
- ↑ "Coroutines (C++20) - cppreference.com" . en.cppreference.com . Consultado el 16 de julio de 2025 .
- ↑ Carter, Casey; Baker, Lewis; Jabot, Corentin. "Implementación de std::generator en C++20" . godbolt.org . Consultado el 16 de julio de 2025 .
- ↑ "std::generator" . cppreference.com .
- ↑ "¿Para qué se utiliza la palabra clave yield en C#?" . stackoverflow.com . Consultado el 1 de enero de 2018 .
- ↑ "Algunos detalles sobre las expresiones de cálculo en F#" . Consultado el 14 de diciembre de 2007 .
- ↑ PEP 380 -- Sintaxis para delegar a un subgenerador
- ↑ Funciones generadoras en R
- ↑ "Generadores infinitos en R" . Fe cartesiana . 5 de enero de 2013.
Referencias
- Stephan Murer, Stephen Omohundro , David Stoutamire y Clemens Szyperski: Abstracción de iteración en Sather . ACM Transactions on Programming Languages and Systems , 18(1):1-15 (1996)
- Estructuras de programación
- Iteración en programación