Engineering2010. 5. 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. 5. 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. 5. 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