C中的生成器

时间:2009-10-28 14:52:24

标签: c

我得到了一些我无法理解的代码。

在将pow2s的g替换为地图的gen结构后,我陷入了困境。从那里开始,我无法看到它如何继续跟踪价值以及如何存储价值。

代码编译并运行。

有人可以帮我理解这段代码吗?谢谢!

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

2 个答案:

答案 0 :(得分:8)

代码显示了如何生成任意数字序列
通过'发生器'

生成器是动态语言(如python)中的流行工具,可以启用一个 迭代任意长序列而不立即分配整个序列。

跟踪

行中发生
p->next = pow2next;
p->continue_from = 0;

告诉 p 它应该调用 pow2next 来获取序列中的下一个项目
continue_from = 0 表示序列开头的位置。

当您拨打 p-&gt; next(p) 时,实际上只需拨打 pow2next p 作为参数。 对于第一次调用,只需返回 1 并将 continue_from 增加到 2

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

在第二次通话( continue_from = 2 )时,它会创建一个新的 map_gen 结构 在一个新的结构 pow2s 上并使用 times2 这个函数:

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

所有进一步的通话都通过 p-&gt; g-&gt; next(p-&gt; g) 使用 times2 map_gen 来检索下一个 值/根据需要创建新的 map_gen 结构。 所有值跟踪都是使用struct-member continue_from 或使用返回代码完成的。

虽然在C中显示了一个有趣的生成器方法,但我必须声明此代码会泄漏内存! 正如您所看到的,它使用 malloc 分配新结构,但它永远不会 free

我希望这个解释不要混淆,即使你刚刚开始学习C.
如果你真的想了解发电机,你可能希望有一个小蟒蛇前课程;)

<强>更新

正如您在评论中所述,似乎没有一个发电机返回值&gt; 2
增加数字的关键在于函数 map_next

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

这样做,而不是返回修复,它应用的数字 p-&gt; f()
(在我们的例子中,函数 times2() p-&gt; g-&gt; next(p-&gt; g)的结果< / EM>

这是一个递归调用。

它会继续在列表中的每个 map_gen 上调用 map_next() ,直到它到达最后一个之一。
最后一个元素将返回一个固定值( 1 2 )。
然后将其传回上一个调用,该调用将对其应用 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 */

您可以清楚地看到最顶层呼叫的结果如何一直传回第一次调用以形成结果。

答案 1 :(得分:0)

它通过增加与times2实例混合的struct mapgen实例的尾部来跟踪该值

每次调用pow2next都会在尾部添加另一对。

这个例子的唯一价值在于说明高级语言为我们做了多少,以及高级概念的天真实现如何会破坏你的表现。