Engineering2010.05.31 00:00
람다 함수 활용(2)

앞선 포스팅에서 람다 함수의 캡쳐(Capture) 기능에 대해 설명 했었습니다. (
여기! 있습니다)
캡쳐 기능을 사용 하면 다음과 같이 활용 할 수 있습니다.

④ 템플릿을 대체하는 데 활용
람다 함수의 캡처 기능을 사용하면 람다 함수 몸체에서 외부 변수들을 마음껏 사용할 수 있을 뿐만 아니라 클로저(Closure)가 되어 함께 묶입니다. 이 기능을 활용하면 기존에 파라미터 타입과 개수 처리를 일반화하기 위해 사용하던 템플릿 사용을 지양할 수 있습니다. 아래 두 함수가 템플릿과 람다 함수를 이용해서 원하는 시점에 호출 되게 하려면 어떻게 하면 될까요?

// 템플릿 객체와 람다 함수를 이용해 간접적으로 호출될 예제 함수들


void Function_Arg1(int arg1)

{

cout << "Function_Arg1 : " << arg1 << endl;

}

 

void Function_Arg2(int arg1, const char* arg2)

{

cout << "Function_Arg2 : " << arg1 << "," << arg2 << endl;

}



위 두 함수를 특정 파라미터와 묶어 특정 시점에 호출해서 사용하고 싶을 경우 템플릿을 이용하면 아래와 같은 방법으로 사용할 수 있습니다.

// 템플릿 객체를 이용해 함수를 인자와 함께 묶어 특정 시점에 호출하는 예


int arg1 = 1004;

const char* arg2 = "Lambda!";

 

vector<ITask*> taskList;

 

// 1. 컨테이너에 담는다.

taskList.push_back( new Task_1<void, int>(&Function_Arg1, arg1) );

taskList.push_back( new Task_2<void, int, const char*>(&Function_Arg2, arg1, arg2) );

// 2. 특정 시점에 컨테이너를 순회하며 실행시킨다.

for( auto i = taskList.begin(); i != taskList.end(); ++i )

{

(*i)->Do();

}



위와 같은 코드로 템플릿 객체를 사용하려면 다음과 같은 코드가 필요합니다.

// 파라미터 1개짜리 함수를 담기 위한 템플릿 클래스


template<typename RetType, typename ArgType1>

class Task_1 : public ITask

{

typedef function<RetType(ArgType1)> FunctionType;

 

public:

Task_1(FunctionType f, ArgType1 a1)

: function_(f), arg1_(a1)

{

}

 

virtual void Do()

{

cout << "Task_1::Do()" << endl;

function_(arg1_);

}

private:

FunctionType   function_;

ArgType1       arg1_;

};

 

// 파라미터 2개짜리 함수를 담기 위한 템플릿 클래스

template<typename RetType, typename ArgType1, typename ArgType2>

class Task_2 : public ITask

{

typedef function<RetType(ArgType1,ArgType2)> FunctionType;

 

public:

Task_2(FunctionType f, ArgType1 a1, ArgType2 a2)

: function_(f), arg1_(a1), arg2_(a2)

{

}

 

virtual void Do()

{

cout << "Task_2::Do()" << endl;



실제 C++ 프로젝트를 진행하다 보면 위와 비슷한 형태의 템플릿 클래스를 자주 구현하게 됩니다. 이 때 발생하는 문제점은 함수의 파라미터가 늘어날 때마다 템플릿 클래스를 추가해 줘야 하고 코드가 직관적이지 않다는 점입니다. BOOST_PP를 이용하면 자동화할 수 있지만 디버깅이 굉장히 어렵고 작성자만 이해할 수 있는 코드가 만들어지곤 합니다. 때론 작성자도 이해 못하죠 ;)

그럼 이런 상황에서 람다 함수를 이용하면 어떨까요?
아래 예제를 보면 람다 함수의 캡처 기능을 이용해 깔끔하게 구현되는 것을 볼 수 있습니다. 뿐만 아니라 실행시킬 함수의 파라미터가 몇 개든 타입이 무엇이든 추가되는 코드는 없습니다. 앞으로 많은 부분에서 람다 함수를 이용해 템플릿 사용을 줄일 방안이 제안 되길 기대해 봅니다.


// 람다 함수를 이용해 함수와 인자를 묶어 특정 시점에 호출하는 예제


int arg1 = 1004;

const char* arg2 = "Lambda!"; 

...
...
typedef function<void(void)> LambdaType;


vector<LambdaType> lambdaList;

 

lambdaList.push_back( [=](){ Function_Arg1(arg1); } );

lambdaList.push_back( [=](){ Function_Arg2(arg1, arg2); } );

 

for_each( lambdaList.begin(), lambdaList.end(), [](LambdaType lambda)

{

lambda();

});




마치면서

4회에 걸쳐『Plus C++0x』람다(Lambda) 이야기를 했습니다. 막연히 람다(Lambda) 의 기능에 대해 설명 하기 보다는 이면에 깔린 배경 개념을 소개 함으로써 현대 프로그래밍 언어가 갖는 특징을 이야기 하고 싶었습니다.

다음 시리즈에서는 우측 값 참조(RValue Reference)에 대해 알아보고 C++0x에서 어떤 의미를 갖는지를 설명하려고 합니다. 람다(Lambda) 관련해서는 많은 내용을 한 번에 준비해서 쓰려니 고생스럽더군요. 이번엔 차근차근 포스팅 하면서 글을 완성할 수 있으면 좋겠네요 ;)

( 마이크로소프트웨어 6월호의 『생각의 직관적인 표현, 람다(Lambda)』를 보시면 보다 잘 정리 되고 추가 된 내용을 보실 수 있습니다. )


고자료
1. MSDN -
http://msdn.microsoft.com/en-us/library/dd293608.aspx
2. MSDN - http://msdn.microsoft.com/en-us/library/dd293599.aspx
3. MSDN - http://channel9.msdn.com/posts/kmcgrath/Lambda-Expressions-in-C/
4. MSDN - http://blogs.msdn.com/vcblog/archive/2008/11/18/stupid-lambda-tricks.aspx
5. Wikipedia - http://en.wikipedia.org/wiki/First-class_function
6. Wikipedia -  http://en.wikipedia.org/wiki/Higher-order_function
7. VSTS 2010 Team Blog -  http://vsts2010.net/category/Language%20Development/C++0x

저작자 표시 비영리 변경 금지
신고
Posted by TedAhn

댓글을 달아 주세요

Engineering2010.05.28 08:48

지난 글에서 이야기 했던 람다(Lambda)의 배경 개념에 대해 알아보겠습니다.

람다 함수는 First-Class Object?
프로그래밍 언어를 이루는 class, struct, int와 같은 개체들 중에서 아래 조건을 만족하면 First-Class Object로 분류합니다. (First-Class Object는 분류의 한 갈래를 의미하는 것이지 상하 관계를 의미하진 않습니다.)


- 변수와 자료구조에 저장하고 사용할 수 있다.

- 함수의 입력 값으로 사용할 수 있다.
- 함수의 반환 값으로 사용할 수 있다.
- 실행 시간에 생성할 수 있다.

즉, 기존 C++에서는 class, struct, int 등이 First-Class Object 였고  아래 예제에서 볼 수 있듯이 C++0x에서 람다 함수(Lambda Function)가 First-Class Object의 조건들을 만족시킵니다.

...
int main()

{

string text = "C++0x Lambda!";

 

// 1. "코드 조각" 변수에 대입하기

function<void()> lambda = [=]()

{                               

cout << text << endl;

};

 

// 2. "코드 조각"을 자료구조에 저장하기

vector< function<void()> > container;

{               

container.push_back( lambda );

container.push_back( [=](){ cout << text << endl; } );

};

 

// 3. "코드 조각"을 함수의 입력 파라미터로 사용하기

for_each( container.begin(), container.end(), [](const function<void()>& f){ f(); } );

 

return 0;

}


위 조건들을 통해 First-Class Object들은 프로그래밍 언어에서 별 다른 제약 조건 없이 변수로 사용할 수 있게 됩니다. 최근 여러 언어들이 함수를 First-Class Object로 제공하는 이유도 함수를 값 처럼 사용할 수 있게 되면 여러 모로 유용하기 때문입니다. 특히 함수는 다른 First-Class Object들을 생성하거나 조작하는 일련의 코드 묶음이기 때문입니다.

C++0x에서는 람다 함수(Lambda Function)를 First-Class Object로 제공함으로써 코드 조각을 일반 변수처럼 사용할 수 있게 해줍니다.



람다 함수는 Higher-Order Function?

함수의 입력 값으로 함수를 전달 받거나 함수의 결과 값으로 함수를 반환할 수 있을 때 Higher-Order Function이라고 합니다. 아래 예제 코드를 보면 쉽게 알 수 있습니다.

 

int main()

{

// 1. 람다 함수를 반환 값으로 한다.

auto g = [](int x) -> function<int (int)>

{          

return [=](int y) { return x + y; };

};

 

// 2. 람다 함수를 입력 값으로 받는다.

auto h = [](const function<int (int)>& f, int z)

{

return f(z) + 1;

};

 

auto a = h( g(7), 8 );

 

cout << a << endl;

}



예제를 보면 h( g(7), 8 )과 같이 표현된 것을 볼 수 있습니다. 마치 수학 책에서 보던 h( g(x), y )처럼 표현할 수 있는 것입니다. 람다 함수는 C++0x에서 First-Class Object 조건을 만족시키기 때문에 Higher-Order Function의 요구사항 또한 만족시킵니다.


람다 함수는 Closure?
클로저(Closure)는 함수를 호출한 상위 코드 블록의 변수들이 호출된 함수와 묶인 것을 뜻합니다. 즉 호출된 함수는 상위 코드 블록의 외부 변수와 묶여 자기만의 상태를 갖게 되는 것입니다.
C++0x에서는 람다 함수(Lambda Function)가 상위 코드 블록의 변수들과 묶여 클로저(Closure)가 될 수 있다. 이때 람다 함수에서 외부 변수들을 참조하는 것을 ‘캡처’라고 말합니다.
캡처(Capture)는 두 가지 방법으로 할 수 있습니다. 람다 시작을 나타내는 [] 기호 사이에 =과 & 기호를 이용할 수 있습니다. [=]은 값 복사를 의미하고 [&]는 값 참조를 의미합니다. 아래 예제 코드를 보시죠.

 

void capture()

{

int a = 0;

int b = 1;

int c = 2;

 

// 1. default 값 복사 캡처. 상위 코드 블록의 지역 변수 모두 값 복사 가능

[=](){ cout << a << “ “ << b << endl; }();  // 0 1 출력

 

// 2. default 값 참조 캡처. 상위 코드 블록의 지역 변수 모두 값 참조 가능

[&](){ cout << a << “ “ << b++ << endl;}(); // 0 1 출력

 

// 3. default 값 복사 캡처, b와 c 참조 캡처

[=, &b, &c](){ cout << a << “ “ << b << “ ” << c << endl; }();  // 0 2 2 출력

}



이번 포스팅에선 몇 가지 배경 개념들을 알아봤는데요, 도움이 되셨는지 모르겠습니다 ;)
다음 글에선 람다 함수의 활용 방안에 대해 알아보겠습니다.

저작자 표시 비영리 변경 금지
신고
Posted by TedAhn

댓글을 달아 주세요

Engineering2010.05.28 08:44

마이크로소프트웨어 6월호에 실릴 C++ 0x 관련 글을 썼습니다.
제목은 『생각의 직관적인 표현, 람다(Lambda)』입니다.  팀 블로그에서는 『Plus C++0x』라는 제목으로 포스팅 하려고 합니다.
기존에 흥배님께서 C++ 0x에 대해 이미 좋은 글들을 많이 쓰셔서 표절하지 않으면서 무엇을 써야 할지 정말 많은 고민을 했답니다. ;)

그래서 약간 Advanced 하면서 불친절하게(?) 특정 주제에 대해 나름대로 재해석해보기로 했습니다. 첫 주제는 람다(Lambda) 로 잡았구요 다음은 우측값 참조(RValue Reference) 에 대해 쓰려고 합니다. 전체적인 아웃라인은 여기를 보시면 됩니다.


C++0x에 함수형 프로그래밍 언어 기능 추가!?
C++를 사용하다가 요즘 인기 있는 Python, C#, Ruby 같은 언어들을 쓰게 되면 무엇보다 직관적이고 편하다는 생각이 듭니다. 이 언어들은 람다(Lambda)와 클로저(Closure) 같은 함수형 프로그래밍 언어의 기능들을 지원함으로써 생각을 보다 직관적이고 효율적인 코드로 표현할 수 있게 합니다.


그렇다면 C++는 어떨까요?
포인터와 함수 객체 그리고 템플릿으로 점철된 C++ 프로그래밍 세계에도 이런 함수형 프로그래밍 언어의 기능들이 추가되면 직관적이고 덜 수고스러운 코딩을 할 수 있을까요?
마침 비주얼 스튜디오 2010이 발표되면서 비주얼 C++ 10가 공개됐습니다. 인텔리센스 기능 향상, 실시간 에러 검사, 병렬 라이브러리와 같은 막강한 기능이 추가됐지만 무엇보다도 C++0x라는 C++의 새로운 표준을 충실히 구현함으로써 C++ 프로그래머의 생산성을 높여 주목을 끌고 있습니다.
특히 C++0x는 언어의 핵심 기능으로 앞서 언급했던 람다(Lambda)와 클로저(Closure)를 기술하고 있고 이를 충실히 구현한 비주얼 C++ 10을 통해 보다 쉽게 사용할 수 있습니다.

이번
『Plus C++0x 람다이야기』를 통해 전달할 핵심 내용은 다음 두 가지 입니다.

C++0x에서 코드 조각(a piece of code)을 일반 변수처럼 사용할 수 있게 됐다.
C++0x에서 클로저(Closure)를 사용할 수 있게 됐다.


아래 코드를 보시죠.

#include <iostream>

#include <vector>

#include <string>

#include <algorithm>

#include <functional>

 

using namespace std;

 

int main()

{

string text = "C++0x Lambda!";

 

// 1. "코드 조각" 변수에 대입하기

function<void()> lambda = [=]()

{                               

cout << text << endl;

};

 

// 2. "코드 조각"을 자료구조에 저장하기

vector< function<void()> > container;

{               

container.push_back( lambda );

container.push_back( [=](){ cout << text << endl; } );

};

 

// 3. "코드 조각"을 함수의 입력 파라미터로 사용하기

for_each( container.begin(), container.end(), [](const function<void()>& f){ f(); } );

 

return 0;

}



위 예제에서 보는 것처럼 이렇게 일반 변수처럼 대입할 수 있고, 자료구조에 저장할 수 있고 함수의 입출력 값으로 사용할 수 있는 코드 조각을 람다(Lambda) 혹은 람다 함수(Lambda Function)라고 합니다.

람다(Lambda)를 이해하고 어떻게 사용하며 활용할지 설명하기 위해 First-Class Object, Higher-Order Function, Closure 같은 프로그래밍 언어 수업 시간에 나올 법한 개념들을 소개하려고 합니다. 개념적인 이해를 통해 람다(Lambda)와 친해져 보시죠 ;)

또 어떻게 사용 할지 몇 가지 사용 패턴을 제안해 볼까 합니다.
(약간 불친절하게 글이 진행됩니다.
http://vsts2010.net에 있는C++0x 관련 글들을 먼저 읽어 볼 것을 권장합니다.)


람다 함수는 함수 안에 정의할 수 있는 이름 없는 함수(anonymous function)? 

...

void outer_function()

{

void inner_function()

{

}

}


위 코드를 보면 outer_function() 함수 안에 inner_function() 함수가 정의되어 있습니다. C++에서 이런 문법 사용이 가능할까요? 물론 사용할 수 없습니다. 즉, 함수 안에서 함수를 선언하고 정의할 수 없습니다. 그러나 람다 함수(Lambda Function)는 함수 안에서 선언하고 정의하고 호출할 수 있습니다. 


void outer_function()

{

// 이름 없는 함수! 람다!

[](){}();

}


위 예제에서 아무 동작을 하지 않는 람다 함수가 선언, 정의, 호출 됐습니다.

다음 편에서 이런 동작을 가능하게 하는 개념적 배경인 First-Class Object, Higher-Order Function, Closure 에 대해 알아보도록 하겠습니다.

저작자 표시 비영리 변경 금지
신고
Posted by TedAhn

댓글을 달아 주세요

Engineering2010.05.23 16:48
마이크로소프트웨어 6월호에 실릴 C++ 0x 관련 글을 썼습니다.
제목은 『생각의 직관적인 표현, 람다(Lambda)』입니다.
기존에
흥배님께서 C++ 0x에 대해 이미 좋은 글들을 많이 쓰셔서 표절하지 않으면서 무엇을 써야 할지 정말 많은 고민을 했답니다. ;)


그래서 약간 Advanced 하면서 불친절하게(?) 특정 주제에 대해 나름대로 재해석해보기로 했습니다.

첫 주제는 람다(Lambda) 로 잡았구요 다음 편은 우측값 참조(RValue Reference) 에 대해 쓰려고 합니다. 


원고 작성에 10일정도 시간이 있었는데요, 마지막날 피를 말리며 쓰게 되더군요.
다음 편은 어떨지... ㅋ


클라우드 컴퓨팅 관련해서 글을 써야 하는데 잠시 외도 중입니다. ;)
아래 브레인스토밍 하면서 작성 했던 요약 글을 옮겼습니다.




시작
하면서

  • 비트겐슈타인 "언어의 한계가 사고의 한계다"
    • 표현력 증가가 필요하다.
    • 언어에 가지 핵심 기능이 추가 되면서 획기적인 변화가 찾아왔다.
      • 생각을 코드로 직관적으로 표현 있도록 Lambda 추가 됐고
      • 객체 복사가 아닌 객체의 메모리 이동을 통해 성능을 향상 시킬 있는 Rvalue를 꼽을 있겠다.
    • C++0x 이런 언어적인 변경이 있었는데 Functional Language 모던 프로그래밍 언어들이 갖춘 Lambda 라는 기능이다.
  • VS2010 발표 되면서 VC++ 10 지원하는 실시간 에러 검사와 같은 막강한 기능이 추가 됐다.
    • 그렇지만 가장 장점은 C++0x 스펙을 충실히 구현했다는 것이다.
  • C++ 프로그래밍 방법에 변화가 오고 있다.
    • 동안 템플릿 인자 추론, 함수 객체의 답답함을 피 할 수 있다는 것.
  • 오늘 이야기는 다음 가지로 요약 있다.
    • C++0x 에서 코드 조각(a piece of code) 변수처럼 사용 있게 됐다.
    • C++0x 에서 클로저(Closure) 사용 있게 됐다.
  • 예제
    • 코드 조각을 변수 처럼 사용하기
      1. 대입
      1. 입력, 반환
      1. 자료 구조에 저장
  • 함수를 변수 처럼 사용 한다. 그리고 지역 변수를 포함할 있는 Capture 기능을 이용해 코딩 스타일을 어떻게 바꿔 있을까?
    • 이렇게 변수 처럼 사용할 있는 코드 조각을 Lambda 혹인 Lambda Function 이라고 한다.
    • 언어로 다른 언어를 만드는 Lisp 기능을 갖고 있기 때문!
  • 오늘은 람다라고 하는 "코드 조각" 주인공이다. 어려운 용어들을 이야기 .
  • First-Class Object, Higher-Order Function, Closure 같은 프로그래밍 언어 수업 시간에 나올 법한 용어를 익혀가다 보면 Lambda 대해 저절로 익히게 뿐만 아니라 현대 프로그래밍 언어의 개념

 

람다 함수는 함수 안에 정의 있는 이름 없는 함수(anonymous function) ?

  • 함수 형태를 띄고 있다.
  • 명세
    • [](){}()
  • 예제
  • 여러 있다.
  • 값을 반환 있다.
  • 인자를 받을 있다.

 

람다 함수 (Lambda Function) First-Class Object ?

  • First-Class Object 개념
    • 변수와 자료구조에 저장 하고 사용 있다.
    • 함수의 입력 값으로 사용 있다.
    • 함수의 반환 값으로 사용 있다.
    • 실행 시간에 생성 있다
  • , 프로그래밍 언어를 이루는 개체들 중에서 조건을 만족하면 First-Calss Object 라고 한다.
  • C++ 에서는 class, struct, int 들이고 C++ 0x 에서 lambda 추가 하면서 모던 프로그래밍 언어의 특징 이었던 함수의 First-Class Object 화가 일부 이루어 것이다.
  • 예제
    • 변수에 할당,
  • 요즘 인기 있는 언어들은 First-Class Object 범위를 늘려가고 있다.
  • 함수형 언어들은 함수가 First-Class Object 이기 때문

 

람다 함수 (Lambda Function) Higher-Order Function ?

  • Higher-Order Function 개념
    • First-Class Object 이기 때문에 higher-order function
  • 예제

 

람다 함수 (Lambda Function) Closure ?

  • Closure 개념 : 닫힘
  • 상위 코드 블록의 변수들을 Capture 있다.
  • 예제
  • = 참조, & 참조
     

이렇게 일반 변수 처럼 있는 코드 조각이 있으면 무엇이 편해 질까?

  • 변수 처럼 사용 있다는 것은
    • 저장 두었다가 원할 꺼내 있다는
    • 동적으로 생성이 가능하다는
    • 지역 변수 처럼 특정 코드 블록 내에서 바로 선언, 할당해서 사용 있다는
  • 상위 코드 블록의 변수들을 Capture 해서 언제든 실행( 또는 평가) 사용 있으면?

 

Lambda 활용

그렇다면 코드 조각을 변수처럼 그리고 클로저 개념을 사용하면 무엇을 있을까?

  • 지역 변수 처럼 코드 조각을 사용 있다
    • 잠깐 쓰고 코드를 지역 변수 처럼 있다.
  • Async 클래스
    • 코드 로직 흐름을 쉽게 만들 있다.
    • 비동기 처리
  • STL 에서 활용
    • 인자로 넘기는
      • Algorithm 에서 인자로 사용 있다.
    • STL Functor 사용이 불편 했던 이유는?
      • 지역 변수 처럼 블록 내에서만 사용해야 것을 외부에 클래스 마냥 구현해야 했다
        • 쓰고 버릴 코드들을..
        • 재활용이 필요 없는데 말이다.
  • Task 클래스
    • 템플릿을 사용하지 않고 함수 개념을 차용
      • 인자 추론이 필요 없음
      • 프로그래밍 스타일에 변화
    • 코딩 스타일의 변화가 필요하다.
      • 예전에는 어떻게서든 함수의 인자로 넘기기 위해 템플릿을 활용했었는데, 이제 Lambda 함수를 넘기면 된다.
      • Lambda Capture 기능을 활용하면 되는 .
      • 템플릿 사용을 줄여 보자.
        • 인자 추론을 Lambda 이용해 해결해 보자!
  • Task Manager 클래스
    • 코드 조각을 담아두고 코드를 지연 실행 시킬 있다.

 

마치며

  • "코드 조각을 변수처럼 사용하도록 사고를 넓혀 보면 어떨까?"
  • ps. 그렇다고 코드 조각을 포인터화 없냐고 물으신다면.. ;)

 

참고 자료

  • VS2010 블로그
  • MSDN

 

저작자 표시 비영리 변경 금지
신고
Posted by TedAhn

댓글을 달아 주세요


티스토리 툴바