Как можно было бы реализовать Отложенные вычисления в C?

Так как Рассол может вывести Ваш граф объектов к строке, это должно быть возможно.

знать, хотя это Текстовые поля в SQLite использует базу данных, кодирующую, таким образом, Вы, возможно, должны были бы преобразовать его в простую строку, прежде чем Вы не засолите.

18
задан nubela 28 October 2009 в 08:42
поделиться

9 ответов

Вы можете попытаться инкапсулировать это в struct :

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);
20
ответ дан 30 November 2019 в 07:55
поделиться

Вы можете передать аргумент в качестве указателя, чтобы позволить функции изменять его без использования возвращаемого значения:

void multiples_of_2(int *i)
{
    *i += 2;
}

И вызывать его:

int i = 0;
multiples_of_2(&i);
1
ответ дан 30 November 2019 в 07:55
поделиться

Проверить setjmp / longjmp

setjmp.h - это заголовок, определенный в C стандартная библиотека для предоставления "нелокальных скачки "или поток управления помимо обычный вызов подпрограммы и возврат последовательность. Парные функции setjmp и longjmp предоставляют это функциональность. Первый setjmp сохраняет окружение вызывающей функции в структуру данных, а затем longjmp может использовать эту структуру для "прыжок" назад к тому месту, где это было создается при вызове setjmp.

( Сопрограммы Lua были реализованы таким образом)

1
ответ дан 30 November 2019 в 07:55
поделиться

Как упомянул Уилл, такие языки, как 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);
}
2
ответ дан 30 November 2019 в 07:55
поделиться
int multiples_of_2() {
    static int i = 0;
    i += 2;
    return i;
}

Статический int i ведет себя как глобальная переменная, но виден только в контексте multiples_of_2 ().

0
ответ дан 30 November 2019 в 07:55
поделиться

Ключ сохраняет состояние функции между вызовами. У вас есть несколько вариантов:

  1. Статическое (или глобальное) состояние. Означает, что последовательность вызовов функции не является реентерабельной, т.е. у вас не может быть рекурсивного вызова самой функции, а также у вас не может быть более одного вызывающего абонента, выполняющего разные последовательности вызовов.

  2. Инициализация (и, возможно, распределение) состояния на или перед первым вызовом и передавая это функции при каждом последующем вызове.

  3. Выполнение умных вещей с помощью setjmp / longjmp, стека или изменяемого кода (где-то есть статья о каррировании функций в C, которая создает объект с код, необходимый для вызова каррированной функции; аналогичный метод может создать объект с состоянием функции и необходимым кодом для его сохранения и восстановления для каждого вызова). ( Править Нашел - http://asg.unige.ch/site/papers/Dami91a.pdf )

Грег цитирует интересную статью выше, которая представляет способ использования статического состояния с синтаксисом, аналогичным оператору yield . Мне он нравился с академической точки зрения, но, вероятно, не стал бы его использовать из-за проблемы с повторным входом и потому, что я все еще удивлен, что печально известное Устройство Даффи даже компилируется ;-).

На практике большие программы на C действительно хотят выполнять вычисления лениво, например, сервер базы данных может захотеть удовлетворить запрос SELECT ... LIMIT 10 , заключив простой запрос SELECT внутри чего-то, что будет возвращать каждую строку, пока не будет возвращено 10, вместо того, чтобы вычислять весь результат и затем отбрасывать большинство из них. Наиболее похожая на 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);
}

Это кажется громоздким для числа, кратного двум, но становится полезным шаблоном для более сложных генераторов. Вы можете усложнить состояние, не перегружая весь код, который использует ваш генератор, деталями. Еще лучшая практика - вернуть непрозрачный указатель в новой функции и (если GC не доступен) предоставить функцию очистки генератора.

Большое преимущество выделения состояния для каждой новой последовательности вызовов - это такие вещи, как рекурсивные генераторы. Например, генератор, который возвращает все файлы в каталоге, вызывая себя в каждом подкаталоге.

char *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;
}

(Вам придется извинить за неправильное использование readdir, и другие. и моя претензия на то, что C имеет защищенную от идиотов поддержку строк.)

1
ответ дан 30 November 2019 в 07:55
поделиться

Я реализовал свой собственный ленивый eval для решения проблемы Хэмминга.

Вот мой код для всех, кому интересно:

#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++;
  }
}
0
ответ дан 30 November 2019 в 07:55
поделиться

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

5
ответ дан 30 November 2019 в 07:55
поделиться

Основной подход - не делать этого. В Python (и C #) метод yield хранит локальное состояние между вызовами, тогда как в C / C ++ и большинстве других языков локальное состояние, хранящееся в стеке, не сохраняется между вызовами, и это фундаментальная разница в реализации. Таким образом, в C вам нужно будет явно сохранять состояние между вызовами в некоторой переменной - либо в глобальной переменной, либо в параметре функции вашего генератора последовательности. Итак, либо:

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

, либо

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

в зависимости от того, есть ли одна глобальная последовательность или много.

Я быстро рассмотрел longjmp и GCC, вычисленные goto и другие нестандартные вещи, и не могу сказать, что рекомендую любой из них за это! В C делайте это способом C.

1
ответ дан 30 November 2019 в 07:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: