2009-10-28 3 views
5

Я получил этот кусок кода, который я не могу понять.Генераторы в C

Я застрял после того, как он заменил pow2s's g на структуру генома карты. И оттуда я не вижу, как он продолжает отслеживать значение и как он хранится.

Код компилируется и запускается.

Может кто-нибудь помочь мне понять этот код? Благодаря!

PS: Я учусь C

Он переведен из кода последующей 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) 

и переведенный код C:

#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

Как насчет того, чтобы делать какие-то заявления printf, чтобы увидеть, что он делает. – 2009-10-28 15:04:37

+0

Можете ли вы предоставить некоторый контекст для этого алгоритма? Т.е. где вы получили этот код? Что он должен демонстрировать? и т. д. –

+0

Я добавил контекст, он переводится с кода питона (генераторы) – nubela

ответ

0

Он отслеживает значение растущей хвост экземпляров struct mapgen, смешанный с times2 экземплярами

Каждый вызов pow2next добавляет ano пара к хвосту.

Единственное значение этого примера - это иллюстрация того, как много языков высокого уровня для нас и как наивная реализация концепций высокого уровня может привести к гибели вашей производительности.

6

код показывает, как вы можете создать произвольную последовательность чисел
посредством «генераторов».

генераторы являются популярным инструментом для динамических языков, таких как Python и дает возможность
итерации по произвольной длинной последовательности без выделения всей последовательности сразу.

отслеживания происходит в линиях

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

который говорит р что он должен вызвать pow2next для получения следующего элемента в последовательности
и continue_from = 0, чтобы указать, где в начале этапа сть.

При вызове p-> следующая (р) это на самом деле просто позвоните pow2next с р, как это параметр. Для первого звонка это просто возвращает и приращение continue_from до .

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

На втором вызове (continue_from = 2) создаст новый map_gen структуры рабочих на свежем структурыpow2s и с помощью функции раз2

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

Все дальнейшие вызовы проходят через p-> g-> следующая (р> г), который использует times2 и map_gen для получения следующего значение/создать новый map_gen структуры при необходимости. Все отслеживание стоимости выполняется с использованием структурного элемента continue_from или с использованием кодов возврата.

Показывая интересный подход к генераторам в C, я должен сказать, что этот код утечки памяти! Как вы можете видеть, что выделяет новые структуры с использованием таНоса, но он никогда не бесплатно «ы их.

Я надеюсь, что это объяснение не смущает, даже если вы только начинаете учиться C.
Если вы действительно хотите понять, генераторы вы можете, как иметь немного питона экс-курс;)

ОБНОВЛЕНИЕ

Как вы указали в своем комментарии ни один из генераторов никогда не возвращает значение> 2.
Ключа к увеличивающемуся числу лежит в функции map_next:

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

Что это делает, вместо того, чтобы вернуться затруднительным, число это относится p-> е()
(в нашем случае функция times2() к результату р-> G-> следующий (р> г).

Это рекурсивный звонок.

Он будет продолжать называть map_next() на каждом map_gen в списке до тех пор, пока не достигнет последний.
Этот последний элемент возвращает значение фиксированного значения (либо , либо).
который затем передается обратно на предыдущий вызов, который будет применяться times2() на него и вернуть результат это абонент, который, в свою очередь, будет применяться times2() на него и вернуть результат это звонящий .... (вы понимаете).

Все эти рекурсивные вызовы суммируются и образуют окончательное значение.
Если распечатать результат каждого pow2next() позвонить, вы получите это:

/* 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 */ 

Вы можете ясно видеть, как результат самого верхнего вызова передается весь путь обратно в первый вызов для формирования результата.

+0

Спасибо за четкое объяснение. Это то, что я понимаю. При создании новых pow2(), call next() -> возвращает 1, call next() -> p-> g = map_gen1 со свежими pow2s, возвращает 2 * 1 call next() -> p- > g = map_gen1, возвращает 2 * 2, создает map_gen2 с FRESH pow2s При следующем вызове мне кажется, что он снова вызовет 2 * 1, но это не похоже? В какой момент я ошибался? – nubela

+0

Я обновил свой ответ, чтобы добавить комментарий. – Shirkrin

Смежные вопросы