2009-10-28 11 views
5

Tengo este trozo de código que realmente no puedo comprender.Generadores en C

me quedé atrapado después de que se sustituyó g de pow2s a la estructura de generación de un mapa. Y a partir de ahí, no puedo ver cómo continúa el seguimiento del valor y cómo se almacena.

El código se compila y se ejecuta.

Puede alguien ayudarme a entender este código? ¡Gracias!

PS: Estoy aprendiendo C

Se traduce el código de seguimiento de Python:

>>> def pow2s(): 
     yield 1 
     for i in map((lambda x:2*x),pow2s()): 
     yield i 
>>> def mymap(f,iter): 
     for i in iter: 
     yield f(i) 

Y el código C traducida:

#include <stdio.h> 
#include <stdlib.h> 

struct gen { // generic structure, the base of all generators 
    int (*next)() ; 
    int continue_from ; 
} ; 

typedef int (*fptr)() ; 

// Each iterator has 3 components: a structure, a constructor for the structure, 
// and a next function 

// map 

struct mapgen { // structure for map 
    int (*next)() ; 
    int continue_from ; // not really required, provided for compatibility 
    fptr f ; 
    struct gen *g ; 
} ; 

int map_next(struct mapgen *p) { // next function for map 
    return p->f(p->g->next(p->g)) ; 
} 

struct gen *map(fptr f, struct gen *g) { // constructor for map iterator 
    struct mapgen *p = (struct mapgen *)malloc(sizeof(struct mapgen)); 
    p->next = map_next; 
    p->continue_from = 0; 
    p->f = f; 
    p->g = g; 
    return (struct gen *)p ; 
} 


// powers of 2 

struct pow2s { // structure 
    int (*next)() ; 
    int continue_from ; 
    struct gen *g ; 
}; 

int times2(int x) { // anonymous lambda is translated into this 
    return 2*x ; 
} 

struct gen *pow2() ; // forward declaration of constructor 

int pow2next(struct pow2s * p){ // next function for iterator 
    switch(p->continue_from) { 
    case 0: 
    p->continue_from = 1; 
    return 1; 
    case 1: 
    p->g = map(times2,pow2()) ; 
    p->continue_from = 2; 
    return p->g->next(p->g) ; 
    case 2: 
    p->continue_from = 2; 
    return p->g->next(p->g) ; 
    } 
}  

struct gen * pow2() { // constructor for pow2 
    struct pow2s * p = (struct pow2s *)malloc(sizeof(struct pow2s)); 
    p->next = pow2next; 
    p->continue_from = 0; 
    return (struct gen *)p; 
} 

// in main, create an iterator and print some of its elements. 

int main() { 
    int i ; 
    struct gen * p = pow2() ; 
    for(i=0;i<10;i++) 
    printf("%d ",p->next(p)) ; 
    printf("\n"); 
} 
+1

¿Qué le parece poner algunas declaraciones printf allí para ver lo que está haciendo. –

+0

¿Puede proporcionar algún contexto para este algoritmo? Es decir, ¿de dónde sacaste este código? ¿Qué se supone que debe estar demostrando? etc. –

+0

he agregado el contexto, se traduce del código python (generadores) – nubela

Respuesta

0

hace un seguimiento del valor por el crecimiento una cola de instancias MAPGEN struct mezclado con casos Times2

Cada llamada a pow2next añade ano ther pair a la cola.

El único valor de este ejemplo es como una ilustración de cómo hacer los lenguajes de alto nivel mucho para nosotros, y cómo la aplicación ingenua de conceptos de alto nivel puede matar a su rendimiento.

6

El código muestra cómo se puede generar una secuencia arbitraria de números
por medio de 'generadores'.

generadores son una herramienta popular en los lenguajes dinámicos como Python y le permiten a uno
iterar sobre una secuencia larga arbitraria sin asignar toda la secuencia a la vez.

El seguimientoocurre en las líneas

p->next = pow2next; 
p->continue_from = 0; 

que cuenta p que debería llamar pow2next para obtener el siguiente elemento de la secuencia
y continue_from = 0 para indicar que al comienzo de la secuencia nce.

Cuando se llama a p-> siguiente (p) que, de hecho, sólo llame pow2next con p ya que es parámetro. Para la primera llamada, esto simplemente devolverá e incrementará continue_from a .

switch(p->continue_from) { 
case 0: 
    p->continue_from = 1; 
    return 1; 
/* ... */ 

En la segunda llamada (continue_from = 2) se creará un nuevo map_gen estructura de trabajo en un struct frescopow2s y utilizando la función times2:

case 1: 
    p->g = map(times2,pow2()) ; 
    p->continue_from = 2; 
    return p->g->next(p->g) ; 
/* ... */ 

Todas las llamadas adicionales pasan por p-> g-> siguiente (p-> g) que utiliza ocasiones2 y map_gen para recuperar el siguiente valor /Crear nuevo map_gen estructuras según sea necesario. Todo el seguimiento de valores se realiza utilizando el miembro struct continue_from o mediante el uso de códigos de retorno.

Al tiempo que se muestra un enfoque interesante para los generadores en C, tengo que decir que este código pierde memoria. Como se puede ver que asigna nuevas estructuras utilizando malloc pero nunca libres 's ellos.

espero que esta explicación no es a confundir incluso si estás empezando a aprender C
Si realmente quiere entender generadores es posible que les gusta tener un poco de pitón ex supuesto;)

ACTUALIZAR

Como indicó en su comentario ninguno de los generadores parece devolver un valor> 2.
La clave para el creciente número radica en la función map_next:

int map_next(struct mapgen *p) { 
    return p->f(p->g->next(p->g)); 
} 

Lo que esto hace es, en lugar de devolver un arreglo, el número se aplica p> f()
(en nuestro caso la función ocasiones2() al resultado de p-> g-> siguiente (p-> g).

Esta es una llamada recursiva.

Se seguirá pidiendo map_next() en cada map_gen en la lista hasta que llega a la última.
Este último elemento devolverá un valor fijo (ya sea o).
que luego se pasa de nuevo a la llamada anterior que se aplicará ocasiones2() en él y devolver el resultado de que es la persona que llama, que a su vez se aplicará ocasiones2() en él y devolver el resultado a es quien llama ... (ya entiendes).

Todas estas llamadas recursivas resumen y forman el valor final.
Si imprime el resultado de cada pow2next() llaman obtendrá esto:

/* first call */ 
    1 
pow2next: returning 1 
pow2next: returning 2 /* times2(1) */ 

    2 
pow2next: returning 1 
pow2next: returning 2 /* times2(1) */ 
pow2next: returning 4 /* times2(2) */ 

    4 
pow2next: returning 1 
pow2next: returning 2 /* times2(1) */ 
pow2next: returning 4 /* times2(2) */ 
pow2next: returning 8 /* times2(4) */ 

8 
pow2next: returning 1 
pow2next: returning 2 /* times2(1) */ 
pow2next: returning 4 /* times2(2) */ 
pow2next: returning 8 /* times2(4) */ 
pow2next: returning 16 /* times2(8) */ 

16 

/* and so on */ 

Se puede ver claramente cómo el resultado de la tapa la mayoría de llamada se pasa todo el camino de vuelta a la primera llamada para formar el resultado.

+0

Gracias por la explicación clara. Esto es lo que entiendo. En creación de nuevos pow2(), llamada next() -> devuelve 1, llamada next() -> p-> g = map_gen1 con pow2s frescas, devuelve 2 * 1 llamada next() -> p- > g = map_gen1, devuelve 2 * 2, crea map_gen2 con FRESH pow2s En la siguiente llamada, me parece que volverá a llamar a 2 * 1, pero no parece? ¿En qué momento lo entendí mal? – nubela

+0

Actualicé mi respuesta para incluir su comentario. – Shirkrin

Cuestiones relacionadas