Python 如何在C中实现惰性评估?

Python 如何在C中实现惰性评估?,python,c,Python,C,比如说, 下面是python代码: def multiples_of_2(): i = 0 while True: i = i + 2 yield i 我们如何将其翻译成C代码 编辑:我希望用C语言将这段python代码翻译成类似的生成器,并使用next()函数。我不想看到的是如何在C中创建一个函数来输出2的倍数。2的倍数仅仅是一个例子来说明C中的惰性求值生成器的问题。我最近发现了一篇很好的文章,其中描述了一种方法。这当然不是为心脏虚弱的人准备的。基本方法是不要这样做。

比如说,

下面是python代码:

def multiples_of_2():
  i = 0
  while True:
    i = i + 2
    yield i
我们如何将其翻译成C代码


编辑:我希望用C语言将这段python代码翻译成类似的生成器,并使用next()函数。我不想看到的是如何在C中创建一个函数来输出2的倍数。2的倍数仅仅是一个例子来说明C中的惰性求值生成器的问题。

我最近发现了一篇很好的文章,其中描述了一种方法。这当然不是为心脏虚弱的人准备的。

基本方法是不要这样做。在Python(和C#)中,“yield”方法在调用之间存储本地状态,而在C/C++和大多数其他语言中,堆栈上存储的本地状态在调用之间不保留,这是一个基本的实现差异。因此,在C语言中,您必须将调用之间的状态显式地存储在某个变量中——要么是全局变量,要么是序列生成器的函数参数。因此,要么:

int multiples_of_2() {
   static int i = 0;
   i += 2;
   return i;
}

取决于是一个全局序列还是多个全局序列

我很快就考虑了longjmp和GCC计算gotos以及其他非标准的东西,我不能说我会为此推荐任何一款!在C中,按C的方式进行

int multiples_of_2() {
    static int i = 0;
    i += 2;
    return i;
}
静态int i的行为类似于全局变量,但仅在_2()的倍数_的上下文中可见。

签出

h是C语言中定义的头文件 提供“非本地”服务的标准图书馆 “跳转”,或控制除 常用子程序调用和返回 序列成对函数setjmp 而longjmp提供了这一点 功能。第一个setjmp保存 调用函数的环境 转换为数据结构,然后 longjmp可以使用此结构 “跳”回到原来的位置 在setjmp调用时创建


(以这种方式实现)

您可以将参数作为指针传递,以允许函数在不使用返回值的情况下修改参数:

void multiples_of_2(int *i)
{
    *i += 2;
}
并称之为:

int i = 0;
multiples_of_2(&i);

您可以尝试将其封装在
结构中

typedef struct s_generator {
    int current;
    int (*func)(int);
} generator;

int next(generator* gen) {
    int result = gen->current;
    gen->current = (gen->func)(gen->current);
    return result;
}
然后使用以下各项定义您的倍数:

int next_multiple(int current) { return 2 + current; }
generator multiples_of_2 = {0, next_multiple};
你通过打电话得到下一个倍数

next(&multiples_of_2);

正如将要提到的,像python这样的语言负责在生成器的连续调用之间存储堆栈状态。因为C没有这种机制,所以您必须自己做。正如格雷格指出的那样,“通用”的方法不适合胆小的人。传统的C方式是自己定义和维护状态,并将其传入和传出方法。因此:

struct multiples_of_two_state {
       int i;
       /* all the state you need should go in here */
};

void multiples_of_two_init(struct multiples_of_two_state *s) {
    s->i = 0;
}

int multiples_of_two_next(struct multiples_of_two_state *s) {
    s->i += 2;
    return s->i;
}

/* Usage */
struct multiples_of_two_state s;
int result;
multiples_of_two_init(&s);
for (int i=0; i<INFINITY; i++) {
    result = multiples_of_two_next(&s);
    printf("next is %d", result);
}
struct是两个状态的乘积{
int i;
/*所有你需要的州都应该在这里*/
};
void乘以\u two\u init(struct乘以\u two\u state*s){
s->i=0;
}
int乘以两个状态的下一个(struct乘以两个状态的下一个){
s->i+=2;
返回s->i;
}
/*用法*/
结构将两个状态的状态乘以;
int结果;
两个初始值的乘积;

对于(int i=0;i,关键是在调用之间保持函数的状态。您有许多选项:

  • 静态(或全局)状态。表示函数调用序列不可重入,即不能递归调用函数本身,也不能让多个调用方运行不同的调用序列

  • 在第一次调用时或之前初始化(并可能分配)状态,并在每次后续调用中将其传递给函数

  • 使用setjmp/longjmp、堆栈或可修改的代码做一些巧妙的事情(有一篇关于在C中使用curry函数的文章,它使用调用curry函数所需的代码创建一个对象;类似的技术可以创建一个对象,其中包含函数的状态以及为每次调用保存和恢复函数所需的代码)。(编辑找到它--)

  • Greg引用了上面一篇有趣的文章,该文章介绍了一种使用静态的方法,其语法类似于
    yield
    语句。我在学术上喜欢它,但可能因为可重入性问题而不使用它,因为我仍然很惊讶臭名昭著的Duffy的设备甚至可以编译;-)

    在实践中,大型C程序确实希望延迟计算,例如,数据库服务器可能希望通过将普通的
    SELECT
    查询包装到某个内容中来满足
    SELECT…LIMIT 10
    查询,该内容将生成每一行,直到返回10为止,而不是计算整个结果,然后丢弃大部分结果类似ost C的技术是显式地为状态创建一个对象,并在每次调用时使用该对象调用函数。例如,您可能会看到如下内容:

    /* Definitions in a library somewhere. */
    typedef int M2_STATE;
    M2_STATE m2_new() { return 0; }
    int m2_empty(M2_STATE s) { return s < INT_MAX; }
    int m2_next(M2_STATE s) { int orig_s = s; s = s + 2; return orig_s; }
    
    /* Caller. */
    M2_STATE s;
    s = m2_new();
    while (!m2_empty(s))
    {
        int num = m2_next(s);
        printf("%d\n", num);
    }
    

    (你必须原谅我对readdir等的滥用,以及我假装C有防白痴字符串支持。)

    我已经实现了自己的惰性评估,以解决hamming问题

    以下是我对任何感兴趣的人的代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    // Hamming problem in C.
    
    typedef struct gen {
      int tracker;
      int (*next)(struct gen *g);
    } generator;
    
    int twos_gen(struct gen *g) {
      g->tracker = g->tracker + 2;
      return g->tracker;
    }
    
    generator* twos_stream() {
      generator *g = malloc(sizeof(generator));
      g->next = twos_gen;
      g->tracker = 0;
      return g;
    }
    
    int threes_gen(struct gen *g) {
      g->tracker = g->tracker + 3;
      return g->tracker;
    }
    
    generator* threes_stream() {
      generator *g = malloc(sizeof(generator));
      g->next = threes_gen;
      g->tracker = 0;
      return g;
    }
    
    int fives_gen(struct gen *g) {
      g->tracker = g->tracker + 5;
      return g->tracker;
    }
    
    generator* fives_stream() {
      generator *g = malloc(sizeof(generator));
      g->next = fives_gen;
      g->tracker = 0;
      return g;
    }
    
    int smallest(int a, int b, int c) {
      if (a < b) {
        if (c < a) return c;
        return a;
      }
      else {
        if (c < b) return c;
        return b;
      }
    }
    
    int hamming_gen(struct gen *g) {
      generator* twos = twos_stream();
      generator* threes = threes_stream();
      generator* fives = fives_stream();
    
      int c2 = twos->next(twos);
      int c3 = threes->next(threes);
      int c5 = fives->next(fives);
    
      while (c2 <= g->tracker) c2 = twos->next(twos);
      while (c3 <= g->tracker) c3 = threes->next(threes);
      while (c5 <= g->tracker) c5 = fives->next(fives);
    
      g->tracker = smallest(c2,c3,c5);
      return g->tracker;
    }
    
    generator* hammings_stream() {
      generator *g = malloc(sizeof(generator));
      g->next = hamming_gen;
      g->tracker = 0;
      return g;
    }
    
    int main() {
      generator* hammings = hammings_stream();
      int i = 0;
      while (i<10) {
        printf("Hamming No: %d\n",hammings->next(hammings));
        i++;
      }
    }
    
    #包括
    #包括
    //C中的Hamming问题。
    类型定义结构生成{
    整数跟踪器;
    int(*下一个)(结构gen*g);
    }发电机;
    int twos_gen(结构gen*g){
    g->tracker=g->tracker+2;
    返回g->tracker;
    }
    生成器*twos_流(){
    发电机*g=malloc(发电机的大小);
    g->next=Two S_gen;
    g->tracker=0;
    返回g;
    }
    int-threes_-gen(结构gen*g){
    g->tracker=g->tracker+3;
    返回g->tracker;
    }
    生成器*三个S_流(){
    发电机*g=malloc(发电机的大小);
    g->next=3S\U gen;
    g->tracker=0;
    返回g;
    }
    内部五层(结构层*g){
    g->tracker=g->tracker+5;
    返回g->tracker;
    }
    生成器*fives_stream(){
    发电机*g=malloc(发电机的大小);
    g->next=五根;
    g->tracker=0;
    返回g;
    }
    最小整数(整数a、整数b、整数c){
    if(achar *walk_next(WALK_STATE *s)
    {
        if (s->subgenerator)
        {
            if (walk_is_empty(s->subgenerator))
            {
                walk_finish(s->subgenerator);
                s->subgenerator = NULL;
            }
            else
                return walk_next(s->subgenerator);
        }
    
        char *name = readdir(s->dir);
        if (is_file(name))
            return name;
        else if (is_dir(name))
        {
            char subpath[MAX_PATH];
            strcpy(subpath, s->path);
            strcat(subpath, name);
            s->subgenerator = walk_new(subpath);
            return walk_next(s->subgenerator);
        }
        closedir(s->dir);
        s->empty = 1;
        return NULL;
    }
    
    #include <stdio.h>
    #include <stdlib.h>
    
    // Hamming problem in C.
    
    typedef struct gen {
      int tracker;
      int (*next)(struct gen *g);
    } generator;
    
    int twos_gen(struct gen *g) {
      g->tracker = g->tracker + 2;
      return g->tracker;
    }
    
    generator* twos_stream() {
      generator *g = malloc(sizeof(generator));
      g->next = twos_gen;
      g->tracker = 0;
      return g;
    }
    
    int threes_gen(struct gen *g) {
      g->tracker = g->tracker + 3;
      return g->tracker;
    }
    
    generator* threes_stream() {
      generator *g = malloc(sizeof(generator));
      g->next = threes_gen;
      g->tracker = 0;
      return g;
    }
    
    int fives_gen(struct gen *g) {
      g->tracker = g->tracker + 5;
      return g->tracker;
    }
    
    generator* fives_stream() {
      generator *g = malloc(sizeof(generator));
      g->next = fives_gen;
      g->tracker = 0;
      return g;
    }
    
    int smallest(int a, int b, int c) {
      if (a < b) {
        if (c < a) return c;
        return a;
      }
      else {
        if (c < b) return c;
        return b;
      }
    }
    
    int hamming_gen(struct gen *g) {
      generator* twos = twos_stream();
      generator* threes = threes_stream();
      generator* fives = fives_stream();
    
      int c2 = twos->next(twos);
      int c3 = threes->next(threes);
      int c5 = fives->next(fives);
    
      while (c2 <= g->tracker) c2 = twos->next(twos);
      while (c3 <= g->tracker) c3 = threes->next(threes);
      while (c5 <= g->tracker) c5 = fives->next(fives);
    
      g->tracker = smallest(c2,c3,c5);
      return g->tracker;
    }
    
    generator* hammings_stream() {
      generator *g = malloc(sizeof(generator));
      g->next = hamming_gen;
      g->tracker = 0;
      return g;
    }
    
    int main() {
      generator* hammings = hammings_stream();
      int i = 0;
      while (i<10) {
        printf("Hamming No: %d\n",hammings->next(hammings));
        i++;
      }
    }