Python 如何在C中实现惰性评估?
比如说, 下面是python代码: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中的惰性求值生成器的问题。我最近发现了一篇很好的文章,其中描述了一种方法。这当然不是为心脏虚弱的人准备的。基本方法是不要这样做。
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++;
}
}