C에서 함수형 프로그래밍을위한 도구는 무엇입니까?


153

나는 C ( C ++가 아닌) 에서 함수형 프로그래밍을 수행하는 방법에 대해 최근 많이 생각했습니다 . 분명히 C는 절차 적 언어이며 실제로 함수형 프로그래밍을 기본적으로 지원하지 않습니다.

함수형 프로그래밍 구문을 언어에 추가하는 컴파일러 / 언어 확장이 있습니까? GCC는 중첩 함수 를 언어 확장으로 제공합니다 . 중첩 함수는 상위 스택 프레임에서 변수에 액세스 할 수 있지만 여전히 성숙한 클로저와는 거리가 멀습니다.

예를 들어, C에서 실제로 유용 할 수 있다고 생각하는 한 가지는 함수 포인터가 예상되는 곳이라면 람다 식을 전달하여 함수 포인터로 붕괴되는 클로저를 만들 수 있다는 것입니다. C ++ 0x는 람다 식을 포함시킬 것입니다 (굉장하다고 생각합니다). 그러나 나는 직선 C에 적용 가능한 도구를 찾고 있습니다.

명확히하기 위해, 함수형 프로그래밍에 더 적합한 C의 특정 문제를 해결하려고하지 않습니다. 내가 원한다면 어떤 도구가 있는지 궁금합니다.


1
C를 그렇지 않은 것으로 바꾸려는 해킹과 이식 불가능한 확장을 찾는 대신 원하는 기능을 제공하는 언어를 사용하는 것이 어떻습니까?
Robert Gamble


@AndyBrice이 질문은 다른 언어에 관한 것이며 실제로 이해가되지 않는 것 같습니다 (C ++에는 같은 의미에서 "생태계"가 없습니다)
Kyle Strand

답변:


40

FFCALL을 사용하면 C에서 클로저를 작성할 수 있습니다 . 호출 callback = alloc_callback(&function, data)과 같은 함수 포인터를 반환합니다 . 그러나 가비지 수집을 수동으로 처리해야합니다.callback(arg1, ...)function(data, arg1, ...)

관련하여, 블록 이 Apple의 GCC 포크에 추가되었습니다. 그것들은 함수 포인터가 아니지만, 손으로 캡처 된 변수를 저장하고 무료로 저장하지 않아도 람다를 전달할 수 있습니다 (효과적으로 일부 복사 및 참조 횟수가 발생하고 일부 구문 설탕 및 런타임 라이브러리 뒤에 숨겨 짐).


89

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; });

12
정말 멋지다. 보인다 __fn__블록 내에서 정의 된 함수에 대한 단지 임의의 이름입니다 ({... })이 아닌 일부 GCC 확장 또는 사전 정의 된 매크로? __fn__GCC 정의와 매우 유사한 이름을 선택하면 실제로 머리를 긁고 GCC 문서를 검색하여 아무런 효과가 없었습니다.
FooF

1
안타깝게도 이것은 실제로 작동하지 않습니다. 클로저가 무엇이든 캡처하기를 원한다면 실행 가능한 스택이 필요합니다. 이는 끔찍한 보안 관행입니다. 개인적으로, 나는 그것이 전혀 유용하다고 생각하지 않습니다.
Demi

원격으로 유용하지는 않지만 재미 있습니다. 그래서 다른 대답이 받아 들여집니다.
Joe D

65

함수형 프로그래밍은 람다가 아니라 순수한 함수에 관한 것입니다. 따라서 다음은 기능적 스타일을 널리 홍보합니다.

  1. 함수 인수 만 사용하고 전역 상태는 사용하지 마십시오.

  2. 부작용 (예 : printf 또는 IO)을 최소화하십시오. 모든 기능에서 직접 부작용을 일으키지 않고 실행될 수있는 IO를 설명하는 데이터를 반환합니다.

마법 없이도 일반 c로 달성 할 수 있습니다.


5
함수 프로그래밍에 대한 극단적 인 관점을 부조리 한 점으로 보았습니다. 예를 들어, map기능을 전달할 기능이없는 순수한 사양은 무엇입니까 ?
Jonathan Leonard

27
이 접근법은 매크로를 사용하여 c 내에서 기능적 언어를 만드는 것보다 훨씬 실용적이라고 생각합니다. 순수한 함수를 적절하게 사용하면 for 루프 대신 맵을 사용하는 것보다 시스템을 개선 할 가능성이 높습니다.
Andy Till

6
확실히 당신은지도가 시작점이라는 것을 알고 있습니다. 일급 기능이 없으면 모든 프로그램 (모든 기능이 '순수한'임에도 불구하고)은 일차 기능과 동등한 것보다 정보 이론상 차수가 더 낮습니다. 필자의 견해로는이 선언적 스타일은 FP의 주요 이점 인 일류 함수에서만 가능합니다. 나는 당신이 누군가에게 장애를 겪고 있다고 생각합니다. 역사적으로 FP라는 용어는 순도 이상의 퍼스트 클래스 펑크를 암시했습니다.
Jonathan Leonard

PS 이전 의견은 모바일에서 나 왔으며 불규칙한 문법은 의도적이지 않았습니다.
Jonathan Leonard

1
Andy Tills의 답변은 사물에 대한 매우 실용적인 견해를 보여줍니다. C에서 "Pure"로 이동하면 멋진 것이 필요하지 않으며 큰 이점이 있습니다. 일등석 기능은 "편안한 생활"을 설명합니다. 편리하지만 순수한 스타일의 프로그래밍을 채택하는 것이 실용적입니다. 나는 왜 아무도이 모든 것과 관련하여 꼬리 호출에 대해 이야기하지 않는지 궁금합니다. 일급 기능을 원하는 사람들은 꼬리 호출을 요청해야합니다.
BitTickler

17

Hartel & Muller의 저서 Functional C 는 현재 http://eprints.eemcs.utwente.nl/1077/ 에서 찾을 수 있습니다 ( PDF 버전에 대한 링크가 있음).


2
이 책에서 기능적인 것은 시도하지 않습니다 (질문과 관련하여).
Eugene Tolmachev

4
당신이 옳은 것 같습니다. 실제로, 서문은이 책의 목적은 학생이 이미 기능적 프로그래밍에 익숙해 졌을 때 명령형 프로그래밍을 가르치는 것이라고 말합니다. 참고로, 이것은 SO의 첫 번째 대답이며 썩은 링크로 이전 답변을 언급하는 평판이 필요하지 않았기 때문에 답변으로 작성되었습니다. 사람들이 왜이 답변을 +1로 유지하는지 궁금합니다. 그렇게하지 마십시오! :-)
FooF

1
아마도이 책은 사후 기능 프로그래밍 (Post-Functional Programming)이라고 더 잘 부쳐 졌을 것입니다. "먼저"Imperative C "가 섹시하게 들리지 않기 때문에, 둘째는 기능적 프로그래밍 패러다임에 익숙하다고 가정하기 때문입니다. 이 기능은 Larry Wall의 기사 / 프레젠테이션 전에 작성되었지만 Larry Wall의 포스트 모더니즘 프로그래밍 개념과 관련이 있습니다.
FooF

그리고 이것은 중복 된 답변입니다
PhilT

8

함수형 프로그래밍 스타일의 전제 조건은 일류 함수입니다. 다음에 견딜 수 있다면 휴대용 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 });

본질적으로 우리는 함수 / 인수와 매크로의 쌍으로 표현 된 클로저로 일등 함수를 모방합니다. 전체 코드는 여기 에서 찾을 수 있습니다 .


7

가장 중요한 것은 코드 생성기를 사용하는 것입니다. 함수형 프로그래밍을 제공 한 다른 언어로 프로그래밍 한 다음 그로부터 C 코드를 생성 하시겠습니까?

그것이 매력적인 옵션이 아니라면 CPP를 남용하여 그 길을 갈 수 있습니다. 매크로 시스템을 사용하면 기능적 프로그래밍 아이디어를 모방 할 수 있습니다. gcc가 이런 식으로 구현되었다고 들었지만 확인한 적이 없습니다.

C는 물론 함수 포인터를 사용하여 함수를 전달할 수 있으며, 주요 문제는 클로저가 부족하고 형식 시스템이 방해를받는 경향이 있습니다. M4와 같은 CPP보다 강력한 매크로 시스템을 탐색 할 수 있습니다. 궁극적으로, 내가 제안하는 것은 진정한 C는 큰 노력없이 작업에 달려 있지 않지만 C를 확장하여 작업에 맞출 수 있다는 것입니다. CPP를 사용하거나 스펙트럼의 다른 쪽 끝으로 이동하여 다른 언어에서 C 코드를 생성 할 수 있다면이 확장은 C와 가장 비슷해 보일 것입니다.


이것이 가장 정직한 답변입니다. 기능적인 방식으로 C를 작성하려는 것은 언어 디자인과 구조 자체에 대항하는 것입니다.
요시야

5

클로저를 구현하려면 어셈블리 언어와 스택 스와핑 / 관리에 만족해야합니다. 그것에 대해 권장하지 않고, 그것이 당신이해야 할 일이라고 말하면됩니다.

C에서 익명 함수를 어떻게 처리할지 확실하지 않습니다. von Neumann 컴퓨터에서는 익명 함수를 asm으로 수행 할 수 있습니다.



2

펠릭스 언어는 C ++로 컴파일합니다. C ++을 신경 쓰지 않는다면 아마도 그것이 단계의 돌이 될 수 있습니다.


9
⁻¹ 때문에 α) 저자는«C ++이 아닌»을 명시 적으로 언급했습니다. β) 컴파일러가 생성 한 C ++은«사람이 읽을 수있는 C ++»이 아닙니다. γ) C ++ 표준은 함수형 프로그래밍을 지원하므로 한 언어에서 다른 언어로 컴파일러를 사용할 필요가 없습니다.
Hi-Angel

매크로 나 그와 비슷한 방식으로 기능적 언어를 생각해 내면 C에 대해 그다지 신경 쓰지 않는다는 것을 자동으로 암시합니다.이 답변은 C ++조차도 C의 맨 위에 매크로 파티로 시작했기 때문에 유효합니다. 그 길을 충분히 내려 가면 새로운 언어로 끝납니다. 그런 다음 백엔드 토론으로 넘어갑니다.
BitTickler

1

꽤 많은 프로그래밍 언어가 C로 작성되었습니다. 그중 일부는 일류 시민으로서의 기능을 지원하며, 해당 지역의 언어는 ecl (embembabble common lisp IIRC), Gnu Smalltalk (gst) (Smalltalk has blocks), 그리고 라이브러리가 있습니다 "클로저"의 경우 (예 : glib2 http://library.gnome.org/devel/gobject/unstable/chapter-signal.html#closure ) 최소한 함수형 프로그래밍에 가깝습니다. 따라서 이러한 프로그래밍 중 일부를 사용하여 기능적 프로그래밍을 수행하는 것이 옵션 일 수 있습니다.

글쎄 또는 당신은 Ocaml, Haskell, Mozart / Oz 등을 배우러 갈 수 있습니다 ;-)

문안 인사


적어도 FP 스타일의 프로그래밍을 잘 지원하는 라이브러리를 사용할 때 무엇이 ​​끔찍한 일인지 말씀해 주시겠습니까?
Friedrich

1

C에서 함수형 프로그래밍을하는 방법은 C로 함수형 언어 인터프리터를 작성하는 것이 었습니다. 나는 "함수 표현 언어 (Function EXpression Language)"의 줄임말로 Fexl이라고 명명했습니다.

인터프리터는 매우 작아서 -O3이 활성화 된 시스템에서 68K까지 컴파일됩니다. 장난감도 아닙니다. 저는 비즈니스를 위해 작성한 모든 새로운 생산 코드 (투자 파트너십을위한 웹 기반 회계)에 사용하고 있습니다.

이제는 (1) 시스템 루틴을 호출하는 내장 함수 (예 : 포크, exec, setrlimit 등)를 추가하거나 (2) Fexl으로 작성할 수있는 함수를 최적화하기 위해 C 코드 만 작성합니다 (예 : 검색). 하위 문자열의 경우).

모듈 메커니즘은 "컨텍스트"개념을 기반으로합니다. 컨텍스트는 심볼을 정의에 매핑하는 함수 (Fexl로 작성)입니다. Fexl 파일을 읽을 때 원하는 컨텍스트로 파일을 해결할 수 있습니다. 이를 통해 사용자 정의 환경을 만들거나 제한된 "샌드 박스"에서 코드를 실행할 수 있습니다.

http://fexl.com


-3

C에서 기능, 구문 또는 의미를 만들고자하는 것은 무엇입니까? 함수형 프로그래밍의 의미는 확실히 C 컴파일러에 추가 될 수 있지만, 완료 될 때까지 Scheme, Haskell 등과 같은 기존 함수형 언어 중 하나와 본질적으로 동일합니다.

이러한 의미를 직접 지원하는 언어의 구문을 배우는 데 시간을 더 잘 사용하는 것이 좋습니다.


-3

C에 대해 모르겠다. Objective-C에는 몇 가지 기능적 기능이 있지만 OSX의 GCC도 일부 기능을 지원하지만 기능 언어를 사용하는 것이 좋습니다. 위에서 언급 한 내용이 많이 있습니다. 나는 개인적으로 계획으로 시작했습니다 .The Little Schemer와 같은 훌륭한 책이 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.