tags:

views:

179

answers:

2
+2  Q: 

Generators in C

I got this chunk of code I can't really comprehend.

I got stuck after it replaced pow2s's g to a map's gen structure. And from there, I can't see how it continues tracking the value, and how it is stored.

The code compiles and runs.

Can someone help me understand this code? Thanks!

PS: I'm learning C

It is translated from the follow Python code:

>>> 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)

And the translated C code:

#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");
}
A: 

It tracks the value by growing a tail of struct mapgen instances mixed with times2 instances

Each call to pow2next adds another pair to the tail.

The only value of this example is as an illustration of how much high level languages do for us, and how naive implementation of high-level concepts can kill your performance.

Arkadiy
+4  A: 
Shirkrin
Thanks for the clear explaination.This is what I understand.On creation of new pow2(),call next() -> returns 1,call next() -> p->g = map_gen1 with fresh pow2s, returns 2*1call next() -> p->g = map_gen1, returns 2*2, creates map_gen2 with FRESH pow2sOn next call, it seems to me that it'll call 2*1 again, but it doesn't seem so? At which point did I get it wrong?
nubela
I updated my answer to include your comment.
Shirkrin