메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

FC++ 배우기: C++ 함수형 프로그래밍 라이브러리

FC++를 사용하여 기본 함수형 프로그래밍 구현하기

Arpan Sen, 선임 소프트웨어 개발자
Arpan은 EDA(Electronic Design Automation) 업계에 몸 담은 소프트웨어 개발자다. 솔라리스, 썬OS, HP-UX, 아이릭스 등과 같은 다양한 유닉스 기종은 물론 리눅스, 마이크로소프트 윈도우까지 다양한 플랫폼을 사용해 왔다. 그의 주된 관심사는 소프트웨어 성능 최적화 기법, 그래프 이론, 병렬 컴퓨팅이다. 기사를 집필하는 이유는 창의적인 욕구를 충족하기 위해서다. Sen은 소프트웨어 시스템 분야에서 대학원 학위를 받았다.
(An IBM developerWorks Contributing Author)

요약:  C++는 일반적으로 OOP(Object-Oriented Programming)와 동의어로 사용되고 있으며 이 언어를 보완하는 유명한 기술 문서도 상당히 많습니다. 이 기사에서는 Yannis Smaragdakis 및 Brian McNamara의 오픈 소스 FC++ 라이브러리를 사용하여 C++로 함수형 프로그래밍을 수행하는 색다른 작업을 시도합니다. FC++를 사용하여 기본 함수 프로그래밍을 구현하는 방법에 대해 배울 수 있습니다.

원문 게재일:  2010 년 8 월 10 일 번역 게재일:   2010 년 11 월 23 일
난이도:  중급 원문:  보기 PDF:  A4 and Letter (50KB | 11 pages)Get Adobe® Reader®
페이지뷰:  3224 회
의견:  


FC++로 함수형 프로그래밍을 시도하는 이유는 무엇인가?

함수형 프로그래밍에는 OOP 등의 다른 프로그래밍 패러다임에 비해 다음과 같은 장점이 있다.

  • 코드의 간결함
  • 부작용이 없는 프로그래밍(무한 set/get 루틴에서 글로벌/정적 변수를 조작하지 않음)
  • 빠른 프로토타입
  • FC++에는 Haskell 프로그래머가 무리 없이 옮겨올 수 있도록 지원하는 다양한 구문 및 라이브러리 함수가 있다.

C++에는 라이브러리를 사용하는 함수형 프로그래밍 구문이 없다. FC++는 기존 C++ 코드와 함께 플러그인할 수 있는 C++ 기반 함수형 프로그래밍 라이브러리의 가장 좋은 오픈 소스 구현이다. FC++는 C++로 함수형 대용량 동기 병렬 프로그래밍을 수행하기 위한 라이브러리인 BSFC++와 같은 프로젝트에서 사용되었다.


다운로드 및 설치

FC++는 SourceForge에서 다운로드할 수 있다(참고자료 참조). 설치된 압축 (.zip) 파일을 언팩하면 헤더 파일 콜렉션이 나타난다. 사용자 애플리케이션 소스에 prelude.h 헤더를 포함시키기만 하면 시작할 준비가 완료된다. Listing 1에서는 FC++ 코드를 사용하는 소스를 컴파일하는 방법을 보여 준다. 이 방법은 헤더 종속적 설치이며 다른 라이브러리가 포함되어 있지 않다.


Listing 1. FC++ 코드를 사용하는 소스 컴파일하기

g++ user_source1.cpp –I<path to FC++ installation>

참고: 이 기사의 모든 코드는 FC++ 1.5와 g++ 3.4.4를 사용하여 테스트되었다.


CFunType 이해하기

함수형 프로그래밍 패러다임에서는 함수가 다른 함수를 인수로 사용할 수 있다. 이에 반해 기본 버전의 C/C++에서는 그러한 구문이 허용되지 않는다. 이 문제점을 해결하기 위해 FC++ 함수는 특정 코드 규칙을 따르는 클래스의 인스턴스로 표현되며, 이를 위해 CFunType이 사용된다. C++ 함수 오브젝트의 특징은 클래스 정의에 operator( )가 있다는 것이다. Listing 2에 이에 대한 예제가 있다.


Listing 2. C++ 함수 오브젝트의 일반적인 사용

struct square { 
   int operator( ) (int x) { return x * x; } 
};

square sqr1;
int result = sqr1(5);

Listing 2 구현의 문제점은 수학적인 용어로 sqr1의 함수 유형은 int —> int이지만 sqr1의 C++ 유형은 struct square라는 것이다. FC++에서는 유형 시그니처 정보를 인코딩하는 데 사용되는 CFunType를 사용한다. CFunType의 마지막 인수는 함수의 리턴 유형이며 나머지 인수는 함수 프로토타입에 표시되는 순서와 동일한 순서의 입력 유형 정보이다. Listing 3에서는 CFunType을 사용하는 square를 보여 준다.


Listing 3. CFunType을 사용하여 사각형 조작을 위한 함수 시그니처 인코딩하기

#include “prelude.h”

struct square : public CFunType<int, int> { 
   int operator( ) (int x) { return x * x; } 
};

square sqr1;
int result = sqr1(5);

Listing 4는 정수를 목록에 삽입하고 업데이트된 목록을 리턴하는 또 다른 예제이다.


Listing 4. CFunType을 사용하여 목록 조작을 위한 함수 시그니처 인코딩하기

#include “prelude.h”

struct Insert : public CFunType<int,List<int>,List<int> > {
   List<int> operator()( int x, const List<int>& l ) const {
    // code for determining where to insert the data goes here
}

참고: Listing 4의 List 데이터 유형은 미리 정의된 FC++ 유형이며, 이에 대해서는 이 기사의 뒷부분에서 설명한다.


함수를 오브젝트로 변환하기

함수에서 함수를 입력 인수로 사용하려면 함수를 오브젝트로 변환해야 한다. FC++는 CFunType을 기반으로 빌드된 클래스의 FunN 카테고리와 실제로 변환을 수행하는 ptr_to_fun 루틴을 정의한다. Listing 5를 살펴보자.


Listing 5. ptr_to_fun을 사용하여 함수를 FC++ 함수 오브젝트로 변환하기

int multiply(int m, int n) { return m*n; }

Fun2<int, int, int> mult1 = ptr_to_fun (&multiply);
int result = mult1(8, 9); 

// result equals 72

CFunType에서처럼 Fun2에 대한 시그니처는 이 오브젝트가 두 개의 정수 입력을 사용하고 정수를 리턴한다는 것을 의미한다. 이와 마찬가지로 Fun3<int, double, double, string>를 사용할 수 있다. 이 시그니처는 하나의 정수와 두 개의 double을 사용하고 문자열을 리턴하는 함수를 나타낸다.


목록 및 lazy 속성 기초

목록 조작은 함수형 프로그래밍의 핵심이다. FC++에는 STL(Standard Template Library) 목록과는 다른 고유한 목록 데이터 유형이 정의되어 있다. FC++ 목록에는 lazy 속성이 있다. FC++에서 무한 요소로 구성된 목록을 작성할 수 있다. 하지만 이러한 목록은 필요한 경우에만 평가된다. Listing 6의 예제를 통해 이 말의 의미를 알 수 있다.


Listing 6. lazy 목록 정의 및 사용하기

List<int> numbers = enumFrom (33);
List<int> even_and_greater_than_33 = filter (even, numbers);
assert (take(4, even_and_greater_than_33)) = list_with (34, 36, 38, 40);

enumFrom, filter, even, takelist_with 요소는 FC++의 미리 정의된 기능 중 일부이다. 위 Listing 6에서 enumFrom은 33부터 시작하는 무한 수 목록을 리턴한다. filter 루틴은 33보다 크거나 같은 수로 구성된 또 하나의 무한 목록을 리턴한다. 마지막으로 take 루틴은 이 목록의 처음 네 개의 요소를 실제로 추출한다. 지금까지 살펴본 어느 목록에서도 무한 수 목록을 저장하지 않으며, 필요한 경우에만 엄격하게 평가가 수행된다.

표 1에서는 FC++에서 목록과 함께 사용되는 일반적인 몇 가지 함수를 설명한다.


표 1. FC++와 함께 사용되는 함수
함수설명
head(<list>)목록의 첫 번째 요소를 리턴한다.
tail(<list>)첫 번째 요소를 제외하고 <list>와 동일한 요소로 구성된 목록을 리턴한다.
cons(<element >, <list>)목록의 맨 앞에 <element>가 추가된 목록을 리턴한다.
NIL빈 목록을 의미한다.
list_with(<element1, element2>,…, <elementN>)N개의 요소로 구성된 목록을 작성한다.
enumFrom(<element1>)element1로 시작하는 무한 목록을 작성한다.
compose(<func1>, <func2>)Compose(f, g)는 f(g(x))를 의미하며 여기서, f(x)와 g(x)는 두 개의 함수이다.
filter(<func1>, <list>)<func1> 함수를 사용하여 필터링된 <list>의 요소 목록을 리턴한다.
take(<N>, <list>)<list>의 처음 N개의 요소로 구성된 목록을 리턴한다.
map(<function>, <list>)첫 번째 <function> 함수를 첫 번째 <list>의 각 요소에 적용한다.

Listing 7은 목록의 내용을 작성 및 표시하는 방법을 보여 주는 또 하나의 예제이다.


Listing 7. 목록 작성, 내용 확인 및 데이터 표시

#include <iostream>
#include “prelude.h”

int main( ) 
{
int x=1, y=2, z=3;
List<int> li = cons(x,cons(y,cons(z,NIL)));

// head also removes the 1st element from the list
assert( head(li) == 1 );

// tail returns whatever is left of in the list, and list_with is
// used to define small sized list
assert( tail(li) == list_with(2,3) );

 while( li ) {
      std::cout << li.head() << " ";
      li = li.tail();
 }
return 0;
}

참고: li 목록을 작성할 때 cons 루틴이 목록의 앞쪽에 요소를 추가한다. 즉, 최종 목록을 작성하기 위해 z, y 및 x 순으로 추가된다.


빠른 목록 구현

FC++ 1.5에는 List 데이터 구조체의 추가 변형인 OddList가 있으며 이 구조체는 list.h에 정의되어 있다. OddListList와 동일한 인터페이스를 가지고 있지만 더 빠르다. List에 대해 작동하는 모든 FC++ 루틴은 OddList에 대해서도 작동한다. OddList는 목록의 다음 노드를 캐싱하기 때문에 빠르게 수행된다. Listing 8에서는 OddList의 사용과 관련된 몇 가지 자세한 특성을 보여 준다.


Listing 8. OddList 사용과 관련된 자세한 특성

OddList<int> odd1 = enumFrom (1);
List<int> list1 = odd1.tail ( ); // always returns List<int>!!

OddList<int> odd2 = enumFrom (1);
List<int> list2 = odd2.delay ( ); // create a List<int> with same data as odd2

List<int> list3 = enumFrom (1);
OddList<int> odd3 = list3.force ( ); // creates an OddList<int> with same data as list3

OddListList용으로 존재하는 STL 스타일 반복자를 지원하지 않는다. OddList 구현에 대한 자세한 정보는 참고자료를 확인한다.


고유 필터 작성하기

Listing 6에서 고유 필터를 작성하려면(예를 들어, 100으로 나눌 수 있고 33보다 큰 모든 수) 고유 필터 함수를 정의한 다음 ptr_to_fun을 호출하여 함수 오브젝트로 변환하면 된다. Listing 9에서 그 방법을 보여 준다.


Listing 9. CFunType을 사용하여 목록 조작을 위한 함수 시그니처 인코딩하기

bool div_by_100 (int n) { 
  return n % 100 ? false : true;
}

List<int> num = enumFrom(34);
List<int> my_nums = filter( ptr_to_fun(&div_by_100), num);

FC++ Listfilter는 기본적으로 완전한 제네릭이므로 모든 데이터 유형을 수용할 수 있다.

이제 커링(currying)과 컴포지션이라는 두 가지 기본적인 함수 기술을 살펴보자.


커링

커링은 일부 함수 인수의 서브세트를 고정된 값에 바인드한 후 새 함수를 작성하는 함수형 프로그래밍 기술이다. Listing 10f 함수를 커링하는 예제이다.


Listing 10. 커링을 사용하여 새 함수 작성하기

int multiply(int m, int n) { return m * n; }
Fun2<int, int, int> f2 = ptr_to_fun (&multiply);
Fun1<int, int> f1 = curry2 (f2, 9);

std::cout << f1(4) << std::endl; // equivalent to multiply(9, 4)

Fun1<int, int> f1_implicit = f2(9); 
std::cout << f1_implicit(4) << std::endl; // same as f1(4)

미리 정의된 curry2 루틴은 f2의 첫 번째 인수를 9에 바인드한다. FC++ 1.5에는 처음 N개의 인수를 특정 값에 고정하는 curry1, curry2curry3 연산자가 있다. 또한 FC++에는 기존 함수의 특정 인수에 값을 미리 지정하여 새 함수를 정의할 수 있는 바인드 루틴이 정의되어 있다. 예를 들어, bind2and3of3 (f, 8, 9)f(x, 8, 9)에 해당하며 여기서, f(x, y, z)는 입력이 3개인 함수이다. 그리고 인수를 지정하는 또 하나 흥미로운 방법으로 밑줄(_)을 사용하는 방법이 있다. 예를 들어, greater (_, 10)는 f(x) = (x > 10)와 같다. greater는 FC++에 미리 정의되어 있다. Listing 11에서는 몇 가지 추가 커링 예제를 보여 준다.


Listing 11. 추가 커링 예제

List<int> integers = enumFrom (1); 
List<int> int_gt_100 = filter(greater(_, 100), integers);

// This list will add 3 to all elements of integers. 
List<int> plus_3 = map (plus(3), integers); 

Listing 12에서는 숫자 자체를 포함한 숫자의 모든 인수를 표시하는 코드 스니펫을 보여 준다.


Listing 12. 숫자의 모든 인수 표시하기

#include "prelude.h"
using namespace fcpp;

#include <iostream>
using namespace std;

bool divisible( int x, int y ) { return x%y==0; }

struct Factors : public CFunType<int,OddList<int> > {
    OddList<int> operator()( int x ) const {
        return filter( curry2(ptr_to_fun(&divisible),x), enumFromTo(1,x) );
    }
} factors;

int main()
{
  OddList<int> odd = factors(20);
  while (odd) { 
      cout << head(odd) << endl;
      odd = tail(odd);
  }
  return 0;
}

Listing 12를 이해하기 위해 가장 중요한 부분은 return filter( curry2(divisible,x), enumFromTo(1,x) ); 행이다. 여기에서는 20을 완전히 나누는 모든 숫자로 구성된 최종 목록을 작성하기 위해 enumFrom(1, 20)에서 리턴된 목록에 적용할 필터를 작성하고 있다. curry2 루틴은 20을 divisible 함수의 첫 번째 인수에 바인드한다. ptr_to_fundivisible 함수를 curry2에 인수로 전달할 수 있는 함수 오브젝트로 변환한다.


컴포지션

함수형 프로그래밍은 기존 코드를 결합하여 새로운 기능을 작성한다. compose() 연산자는 h(x) = f(g(x))와 같이 두 개의 단항 함수인 f(x)g(x)를 작성하여 새 함수 h(x)를 생성한다. 예를 들어, 목록에 대한 compose(head, tail)은 목록의 두 번째 요소를 리턴한다. 이는 함수형 코딩에 적합한 방법이며, g(x)f(x)의 인수로 사용된다. "Functional Programming with the FC++ Library"(참고자료 참조)에서 가져온 Listing 13은 컴포지션을 사용하는 예제이다.


Listing 13. composetail을 사용하여 목록의 두 번째 요소 가져오기

std::string s=”foo”, t=”bar”, u=”qux”;
List<std::string> ls = cons(s, cons(t, cons(u, NIL)));

ls = compose(tail, tail) (ls); // tail(tail(ls));
assert (head(ls) == ”qux”); // s, t are removed 

Listing 14는 목록의 모든 요소를 2씩 증가시키는 또 하나의 예제이다.


Listing 14. compose를 사용하여 목록 요소 증가시키기

List<int> integers = enumFrom (1); 
map (compose(inc, inc), integers);
// this modifies integers to an infinite list [3, 4, 5 ...] 


람다 함수

함수형 프로그래밍에 대한 논의에서는 람다 함수를 언급하지 않을 수 없다. 람다 추상화는 익명 함수를 정의하는 데 사용된다. 이 방법은 간단한 코드에서 별도의 함수를 정의하지 않으려는 경우에 유용하다. 코드에서 람다 기능을 사용하려면 FCPP_ENABLE_LAMBDA 매크로를 정의해야 한다. Listing 15에서는 기존 코드를 활용하여 새로운 수학 및 논리 함수를 간략하게 정의한다. factorial을 정의하는 방법을 자세히 살펴보기 바란다.


Listing 15. 람다 함수 정의하기

// a new function where f(x) = 3*x+1
lambda(X)[ plus[multiplies[3,X],1] ]  
   
// a new function where f(x) = x! (factorial x)
lambda(X)[ l_if[equal[X,0],1,multiplies[X,SELF[minus[X,1]]]] ]

Listing 15는 그 자체로 쉽게 이해할 수 있다. plus, multiplies 등의 루틴은 FC++ 라이브러리의 일부로 정의되어 있으며 개발자는 lambda 연산자를 사용하여 기존 코드를 기반으로 새 함수를 작성할 수 있다.


결론

FC++에서 제공하는 기능은 다음과 같다.

  • 함수형 프로그래밍 요구를 충족하기 위해 쉽게 확장할 수 있는 CFunType 유형의 오브젝트
  • 잠재적으로 무한 시퀀스를 보유할 수 있는 lazy 목록의 구현
  • head, tail, map, filter, ptr_to_fun 등의 여러 함수형 프로그래밍 연산자
  • 커링 연산자, lambda 또는 compose를 사용하여 기존 함수를 기반으로 새 함수를 작성할 수 있는 기능

FC++의 단점이라면 헤더에 정의된 함수를 설명하는 표준화된 문서가 없다는 점일 것이다. 이 기사에서는 compose, curry, bind, take, map, ptr_to_funfilter 등의 가장 유용한 함수를 살펴보았다.


참고자료

교육

제품 및 기술 얻기

  • FC++를 다운로드할 수 있다.

  • FC++ client를 다운로드하여 작업을 자세히 살펴보자.

  • 자신에게 가장한 적합한 방법으로 IBM 제품을 평가해 보자. 시험판 제품을 다운로드하거나, 온라인으로 제품을 사용해 보거나, 클라우드 환경에서 제품을 사용하거나, SOA Sandbox에서 SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.

토론

필자소개

developerWorks Contributing author level

Arpan은 EDA(Electronic Design Automation) 업계에 몸 담은 소프트웨어 개발자다. 솔라리스, 썬OS, HP-UX, 아이릭스 등과 같은 다양한 유닉스 기종은 물론 리눅스, 마이크로소프트 윈도우까지 다양한 플랫폼을 사용해 왔다. 그의 주된 관심사는 소프트웨어 성능 최적화 기법, 그래프 이론, 병렬 컴퓨팅이다. 기사를 집필하는 이유는 창의적인 욕구를 충족하기 위해서다. Sen은 소프트웨어 시스템 분야에서 대학원 학위를 받았다.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=AIX와 UNIX
ArticleID=588003
ArticleTitle=FC++ 배우기: C++ 함수형 프로그래밍 라이브러리
publish-date=08102010
author1-email=arpan@syncad.com
author1-email-cc=

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.