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

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

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

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

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

См. Также связанный вопрос: <stackoverflow.com/questions/24995/…>

Andy Brice 21.10.2008 11:35

@AndyBrice Этот вопрос касается другого языка и на самом деле не имеет смысла (C++ не имеет «экосистемы» в том же смысле, что и.

Kyle Strand 20.01.2016 08:46

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

Robert Gamble 19.10.2008 09:27
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
157
3
51 591
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

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

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

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

Это самый честный ответ. Попытка написать C функционально - это борьба с самим дизайном и структурой языка.

josiah 10.09.2017 00:19

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

Ну или вы можете пойти изучать Ocaml, Haskell, Mozart / Oz или тому подобное ;-)

С уважением

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

Friedrich 27.08.2010 18:32

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

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

Ответ принят как подходящий

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

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

Посмотрите книгу Хартеля и Мюллера, Функциональный C

http://www.ub.utwente.nl/webdocs/ctit/1/00000084.pdf
http://www.cs.bris.ac.uk/~henkm/f2c/index.html

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

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

Не знаю о C. В Objective-C есть некоторые функциональные возможности, GCC на OSX также поддерживает некоторые функции, однако я бы снова рекомендовал начать использовать функциональный язык, их много, упомянутого выше. Я лично начал со схемы, есть несколько отличных книг, таких как «Маленький программист», которые могут вам в этом помочь.

Вы можете использовать вложенные функции 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; });

Это действительно круто. Кажется, __fn__ - это просто произвольное имя функции, определенной в блоке ({ ... }), а не какое-то расширение GCC или предопределенный макрос? Выбор имени для __fn__ (очень похожего на определение GCC) действительно заставил меня почесать затылок и поискать в документации GCC без хорошего эффекта.

FooF 15.08.2012 09:08

К сожалению, на практике это не работает: если вы хотите, чтобы закрытие что-то захватило, для этого потребуется исполняемый стек, что является ужасной практикой безопасности. Лично я не думаю, что это вообще полезно.

Demi 02.08.2017 05:53

Это бесполезно, но весело. Поэтому принимается другой ответ.

Joe D 02.08.2017 13:16

Это весело, но бесполезно

rokstar 31.07.2020 19:33

Язык Феликса компилируется в C++. Может быть, это может быть ступенькой, если вы не против C++.

⁻¹ потому что α) Автор прямо упомянул «не C++», β) C++, созданный компилятором, не будет «C++ для чтения человеком». γ) Стандарт C++ поддерживает функциональное программирование, нет необходимости использовать компилятор с одного языка на другой.

Hi-Angel 20.01.2015 09:52

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

BitTickler 13.11.2018 15:55

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

В этой книге нет попытки придумать что-либо функциональное (что касается вопроса).

Eugene Tolmachev 09.12.2014 01:42

Похоже, вы совершенно правы. Действительно, в предисловии говорится, что цель книги - обучить императивному программированию после того, как студент уже ознакомился с функциональным программированием. К вашему сведению, это был мой первый ответ в SO, и он был написан как ответ только потому, что мне не требовалась репутация, чтобы прокомментировать предыдущий ответ с гнилостной ссылкой. Я всегда удивляюсь, почему люди добавляют +1 к этому ответу ... Пожалуйста, не делайте этого! :-)

FooF 09.12.2014 07:12

Возможно, книгу лучше назвать постфункциональным программированием (во-первых, потому что «Императивный C» не звучит привлекательно, во-вторых, потому что он предполагает знакомство с парадигмой функционального программирования, в-третьих, как каламбур, потому что императивная парадигма кажется упадком стандартов и правильная функциональность и, возможно, в-четвертых, чтобы отсылать к концепции постмодернистского программирования Ларри Уолла - хотя эта книга была написана до статьи / презентации Ларри Уолла).

FooF 09.12.2014 07:20

И это дублированный ответ

PhilT 11.07.2019 12:49

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

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

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

Это может быть достигнуто с помощью простого c, без магии.

Я думаю, вы довели этот крайний взгляд на функциональное программирование до абсурда. Что хорошего в чистой спецификации, например, map, если у кого-то нет средств для передачи ей функции?

Jonathan Leonard 18.05.2014 08:34

Я думаю, что этот подход гораздо более прагматичен, чем использование макросов для создания функционального языка внутри c. Правильное использование чистых функций с большей вероятностью улучшит систему, чем использование map вместо циклов for.

Andy Till 18.05.2014 19:50

Наверняка вы знаете, что карта - это только отправная точка. Без функций первого класса любая программа (даже если все ее функции «чистые») будет иметь более низкий порядок (в смысле теории информации), чем ее эквивалент с функциями первого класса. На мой взгляд, именно этот декларативный стиль, возможный только с функциями первого класса, является основным преимуществом FP. Я думаю, вы оказываете кому-то медвежью услугу, подразумевая, что многословие, возникающее из-за отсутствия первоклассных функций, - это все, что может предложить FP. Следует отметить, что исторически термин FP подразумевал больше первоклассных функций, чем чистоту.

Jonathan Leonard 19.05.2014 00:23

P.S. Предыдущий комментарий был с мобильного телефона - неправильная грамматика не была намеренной.

Jonathan Leonard 19.05.2014 09:56

Я не против первоклассных функций, пользуюсь ими каждый день и без них не обойтись. Впрочем, никогда не было, удачи в этом.

Andy Till 22.05.2014 01:33

Разве это не называется «чистым функциональным программированием»? Я бы сказал, что функциональное программирование - это представление о программе как о рекурсивной композиции функций, и чистота ей ортогональна. Purity помогает вам (или компьютеру) рассуждать об этом, для подтверждения правильности и для оптимизации. Я думаю, что первоклассные функции необходимы, чтобы делать полезные вещи или, по крайней мере, делать их хорошо, но этого недостаточно.

leewz 17.02.2016 03:04

Ответ Энди Тиллса отражает очень прагматичный взгляд на вещи. Переход на «чистый» в языке С не требует ничего особенного и приносит большую пользу. Первоклассные функции по сравнению с «комфортным проживанием». Это удобно, но использование чистого стиля программирования практично. Интересно, почему никто не обсуждает хвостовые вызовы в отношении всего этого. Те, кому нужны первоклассные функции, также должны попросить хвостовые вызовы.

BitTickler 13.11.2018 15:50

Предпосылкой для функционального стиля программирования является первоклассная функция. Это можно было бы смоделировать на портативном 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 });

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

Способ, которым я занимался функциональным программированием на C, заключался в написании интерпретатора функционального языка на C. Я назвал его Fexl, что сокращенно от «Function EXpression Language».

Интерпретатор очень маленький, компилируется до 68 КБ в моей системе с включенным -O3. Это тоже не игрушка - я использую ее для всего нового производственного кода, который пишу для своего бизнеса (веб-учет для инвестиционных партнерств).

Теперь я пишу код на C только для того, чтобы (1) добавить встроенную функцию, вызывающую системную процедуру (например, fork, exec, setrlimit и т. д.), Или (2) оптимизировать функцию, которая в противном случае могла бы быть написана на Fexl (например, search для подстроки).

Механизм модуля основан на концепции «контекста». Контекст - это функция (написанная на Fexl), которая сопоставляет символ с его определением. Когда вы читаете файл Fexl, вы можете разрешить его с любым контекстом, который вам нравится. Это позволяет вам создавать собственные среды или запускать код в ограниченной «песочнице».

http://fexl.com

Другие вопросы по теме