Какие инструменты существуют для функционального программирования в C?

В последнее время я много думал о том, как заниматься функциональным программированием на C (а не на С++). Очевидно, что C является процедурным языком и на самом деле не поддерживает функциональное программирование изначально.

Существуют ли какие-либо расширения для компилятора/языка, которые добавляют некоторые функциональные программирующие конструкции к языку? GCC предоставляет вложенные функции в качестве расширения языка; вложенные функции могут обращаться к переменным из родительского стека, но это еще далеко от зрелых закрытий.

Например, одна вещь, которая, по моему мнению, может быть действительно полезна в C, заключается в том, что везде, где ожидается указатель на функцию, вы можете передать лямбда-выражение, создавая закрытие, которое распадается на указатель функции. С++ 0x будет включать в себя лямбда-выражения (которые, как мне кажется, удивительны); однако, я ищу инструменты, применимые к прямой C.

[Edit] Чтобы уточнить, я не пытаюсь решить конкретную проблему в C, которая больше подходит для функционального программирования; Мне просто интересно, какие инструменты есть, если я захочу сделать это.

Ответ 1

FFCALL позволяет строить замыкания в C - callback = alloc_callback(&function, data) возвращает указатель на функцию, так что callback(arg1, ...) эквивалентно вызову function(data, arg1, ...). Однако вам придется обрабатывать сборку мусора вручную.

Кроме того, блоки добавлены в Apple fork GCC; они не являются указателями на функции, но они позволяют вам проходить вокруг лямбда, избегая при этом необходимости создавать и свободно хранить захваченные переменные вручную (фактически происходит некоторое копирование и подсчет ссылок, скрытые за некоторыми синтаксическими сахарами и библиотеками времени исполнения).

Ответ 2

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

#define lambda(return_type, function_body) \
  ({ \
    return_type anon_func_name_ function_body \
    anon_func_name_; \
  })

Используйте это:

int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });

Ответ 3

Функциональное программирование не касается лямбда, это все о чистых функциях. Таким образом, в широком смысле продвигайте функциональный стиль:

  • Используйте только аргументы функции, не используйте глобальное состояние.

  • Сведите к минимуму побочные эффекты, то есть printf, или любой IO. Возвращает данные, описывающие IO, которые могут быть выполнены вместо того, чтобы вызывать побочные эффекты непосредственно во всех функциях.

Это может быть достигнуто в обычном c, не нужно для магии.

Ответ 4

Книга Хартеля и Мюллера, Функциональный С, может теперь (2012-01-02) найти по адресу: http://eprints.eemcs.utwente.nl/1077/ (есть ссылка на PDF-версию).

Ответ 5

Главное, что приходит на ум, - это использование генераторов кода. Готовы ли вы программировать на другом языке, обеспечивающем функциональное программирование, а затем сгенерировать код C из этого?

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

C может, конечно, передавать функции вокруг с помощью указателей функций, основными проблемами являются отсутствие замыканий, и система типов имеет тенденцию мешать. Вы можете исследовать более мощные макросистемы, чем CPP, такие как M4. Я предполагаю, что в конечном счете, я предлагаю, что истинный C не справляется с задачей без особых усилий, но вы можете расширить C, чтобы он соответствовал задаче. Это расширение будет больше похоже на C, если вы используете CPP, или можете перейти на другой конец спектра и сгенерировать код на другом языке.

Ответ 6

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

Не уверен, как вы будете обрабатывать анонимные функции в C. На машине фон Неймана вы можете делать анонимные функции в asm, однако.

Ответ 8

Хорошо, что на C. написано несколько языков программирования. Некоторые из них поддерживают функции как граждан первого класса, языки в этой области - ecl (embbedabble common lisp IIRC), Gnu Smalltalk (gst) (Smalltalk имеет блоки), тогда есть библиотеки для "закрытий", например, в glib2 http://library.gnome.org/devel/gobject/unstable/chapter-signal.html#closure которые, по крайней мере, приблизились к функциональному программированию. Поэтому, возможно, использование некоторых из этих реализаций для функционального программирования может быть вариантом.

Ну или вы можете учиться Окамлу, Хаскелу, Моцарту/Озу или тому подобное; -)

Привет

Ответ 9

Необходимым условием для стиля функционального программирования является функция первого класса. Он может быть смоделирован в портативном C, если вы переносите следующее:

  • ручное управление привязками лексических областей, а также закрытие.
  • ручное управление временем жизни функциональных переменных.
  • альтернативный синтаксис приложения/вызова функции.
/* 
 * with constraints desribed above we could have
 * good approximation of FP style in plain C
 */

int increment_int(int x) {
  return x + 1;
}

WRAP_PLAIN_FUNCTION_TO_FIRST_CLASS(increment, increment_int);

map(increment, list(number(0), number(1)); // --> list(1, 2)


/* composition of first class function is also possible */

function_t* computation = compose(
  increment,
  increment,
  increment
);

*(int*) call(computation, number(1)) == 4;

время выполнения для такого кода может быть меньше одного

struct list_t {
  void* head;
  struct list_t* tail;
};

struct function_t {
   void* (*thunk)(list_t*);
   struct list_t* arguments;
}

void* apply(struct function_t* fn, struct list_t* arguments) {
  return fn->thunk(concat(fn->arguments, arguments));
}

/* expansion of WRAP_PLAIN_FUNCTION_TO_FIRST_CLASS */
void* increment_thunk(struct list_t* arguments) {
  int x_arg = *(int*) arguments->head;
  int value = increment_int(x_arg);
  int* number = malloc(sizeof *number);

  return number ? (*number = value, number) : NULL;
}

struct function_t* increment = &(struct function_t) {
  increment_thunk,
  NULL
};

/* call(increment, number(1)) expands to */
apply(increment, &(struct list_t) { number(1), NULL });

В сущности, мы имитируем функцию первого класса с замыканиями, представленными как пара функций/аргументов плюс связка макросов. Полный код можно найти здесь.

Ответ 10

Felix language компилируется в С++. Возможно, это может быть шаг за шагом, если вы не против С++.

Ответ 11

Dont Know about C. В Objective-C есть некоторые функциональные возможности, хотя GCC на OSX также поддерживает некоторые функции, однако я бы рекомендовал начать использовать функциональный язык, есть много упоминаний выше. Я лично начал с схемы, есть отличные книги, такие как The Little Schemer, которые могут вам помочь.

Ответ 12

Что такое C, который вы хотите сделать функциональным, синтаксисом или семантикой? Семантика функционального программирования, безусловно, может быть добавлена ​​в компилятор C, но к тому времени, когда вы закончите, у вас будет по существу эквивалент одного из существующих функциональных языков, таких как Scheme, Haskell и т.д.

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