나는 C ( C ++가 아닌) 에서 함수형 프로그래밍을 수행하는 방법에 대해 최근 많이 생각했습니다 . 분명히 C는 절차 적 언어이며 실제로 함수형 프로그래밍을 기본적으로 지원하지 않습니다.
함수형 프로그래밍 구문을 언어에 추가하는 컴파일러 / 언어 확장이 있습니까? GCC는 중첩 함수 를 언어 확장으로 제공합니다 . 중첩 함수는 상위 스택 프레임에서 변수에 액세스 할 수 있지만 여전히 성숙한 클로저와는 거리가 멀습니다.
예를 들어, C에서 실제로 유용 할 수 있다고 생각하는 한 가지는 함수 포인터가 예상되는 곳이라면 람다 식을 전달하여 함수 포인터로 붕괴되는 클로저를 만들 수 있다는 것입니다. C ++ 0x는 람다 식을 포함시킬 것입니다 (굉장하다고 생각합니다). 그러나 나는 직선 C에 적용 가능한 도구를 찾고 있습니다.
명확히하기 위해, 함수형 프로그래밍에 더 적합한 C의 특정 문제를 해결하려고하지 않습니다. 내가 원한다면 어떤 도구가 있는지 궁금합니다.
답변
FFCALL을 사용하면 C에서 클로저를 작성할 수 있습니다 . 호출 callback = alloc_callback(&function, data)
과 같은 함수 포인터를 반환합니다 . 그러나 가비지 수집을 수동으로 처리해야합니다.callback(arg1, ...)
function(data, arg1, ...)
관련하여, 블록 이 Apple의 GCC 포크에 추가되었습니다. 그것들은 함수 포인터가 아니지만, 손으로 캡처 된 변수를 저장하고 무료로 저장하지 않아도 람다를 전달할 수 있습니다 (효과적으로 일부 복사 및 참조 횟수가 발생하고 일부 구문 설탕 및 런타임 라이브러리 뒤에 숨겨 짐).
답변
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; });
답변
함수형 프로그래밍은 람다가 아니라 순수한 함수에 관한 것입니다. 따라서 다음은 기능적 스타일을 널리 홍보합니다.
-
함수 인수 만 사용하고 전역 상태는 사용하지 마십시오.
-
부작용 (예 : printf 또는 IO)을 최소화하십시오. 모든 기능에서 직접 부작용을 일으키지 않고 실행될 수있는 IO를 설명하는 데이터를 반환합니다.
마법 없이도 일반 c로 달성 할 수 있습니다.
답변
Hartel & Muller의 저서 Functional C 는 현재 http://eprints.eemcs.utwente.nl/1077/ 에서 찾을 수 있습니다 ( PDF 버전에 대한 링크가 있음).
답변
함수형 프로그래밍 스타일의 전제 조건은 일류 함수입니다. 다음에 견딜 수 있다면 휴대용 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 코드를 생성 하시겠습니까?
그것이 매력적인 옵션이 아니라면 CPP를 남용하여 그 길을 갈 수 있습니다. 매크로 시스템을 사용하면 기능적 프로그래밍 아이디어를 모방 할 수 있습니다. gcc가 이런 식으로 구현되었다고 들었지만 확인한 적이 없습니다.
C는 물론 함수 포인터를 사용하여 함수를 전달할 수 있으며, 주요 문제는 클로저가 부족하고 형식 시스템이 방해를받는 경향이 있습니다. M4와 같은 CPP보다 강력한 매크로 시스템을 탐색 할 수 있습니다. 궁극적으로, 내가 제안하는 것은 진정한 C는 큰 노력없이 작업에 달려 있지 않지만 C를 확장하여 작업에 맞출 수 있다는 것입니다. CPP를 사용하거나 스펙트럼의 다른 쪽 끝으로 이동하여 다른 언어에서 C 코드를 생성 할 수 있다면이 확장은 C와 가장 비슷해 보일 것입니다.
답변
클로저를 구현하려면 어셈블리 언어와 스택 스와핑 / 관리에 만족해야합니다. 그것에 대해 권장하지 않고, 그것이 당신이해야 할 일이라고 말하면됩니다.
C에서 익명 함수를 어떻게 처리할지 확실하지 않습니다. von Neumann 컴퓨터에서는 익명 함수를 asm으로 수행 할 수 있습니다.