본문 바로가기

고급C,C++

함수 객체(Functor)

많은 STL 안고리즘들이 펑크너라고 부르는 함수 객체를 사용한다. 평크터는 함수처럼 () 과 함께 사용할 수 있는 객체이다. 일반 함수의 이름, 함수를 지시하는 포인터, () 연산자가 오버로딩된 클래스 객체 - 즉 operator() ()라는 이상한 모양의 함수가 정의된 클래스들이 모두 펑크터가 될 수 있다. 

class Linear
{
private:
double slope;
double y0;
public:
Linear(double _s1 = 1, double _y = 0)
:slope(_s1), y0(_y) {}
double operator()(double x){return y0 + slope*x;}
};
이제 오버로딩된 () 연산자가 Linear 객체들을 함수처럼 사용하는 것을 허용한다.
Linear f1;
Linear f2(2.5, 10.0);
double y1 = f1(12.5);
double y2 = f2(0.4);

  • 펑크너 개념
    • 제너레이터는 전달인자 없이 호출하는 함수이다.
    • 단항함수(unary function)는 하나의 전달인자로 호출하는 함수이다.
    • 이항함수(binary function)는 두 개의 전달인자로 호출하는 함수이다.

    • bool 값을 리턴하는 단항 함수는 조건(predicate)이다.
    • bool 값을 리턴하는 이항 함수는 이항 조건(binary predicate)이다.

    • 하나의 값을 함수 전달인자 예제
//functor.cpp -- 펑크터를 사용한다.
#include <iostream>
#include <list>
#include <iterator>

template<class T>
class TooBig
{
private:
T cutoff;
public:
TooBig(const T & t) : cutoff(t){}
bool operator()(const T & v){return v > cutoff;}
};

int main()
{
using namespace std;
TooBig<int> f100(100);
list<int> yadayada;
list<int> etcetera;
int vals[10] = {50, 100, 90, 180, 60, 210, 415, 88, 188, 201};

yadayada.insert(yadayada.begin(), vals, vals+10);
etcetera.insert(etcetera.begin(), vals, vals+10);
ostream_iterator<int, char> out(cout, " ");
cout<<"원래의 리스트: \n";
copy(yadayada.begin(),yadayada.end(),out);
cout<<endl;
copy(etcetera.begin(),etcetera.end(),out);
cout<<endl;

yadayada.remove_if(f100);
etcetera.remove_if(TooBig<int>(200));
cout<<"정비된 리스트: \n";
copy(yadayada.begin(), yadayada.end(), out);
cout<<endl;
copy(etcetera.begin(), etcetera.end(), out);
cout<<endl;

cin.get();
return 0;
}

    • 두 개의 전달인자를 사용하는 템플릿 함수를 이미 가지고 있다고 가정하자, 클래스를 사용하여 이것을 하나의 전달인자를 사용하는 함수 객체로 변환할 수 있다.
      • template<class T>
        bool tooBig(const T & val, const T & lim)
        {
             return val > lim;
        }
      • template<class T>
        class TooBig2
        {
        private:
           T cutoff;
        public:
           TooBig2(const T & t) : cutoff(t) {}
           bool operator() (const T & v) { return tooBig<T>(v,cutoff);}
        }

      • 이제 다음과 같이 사용할 수 있다.
        TooBig2<int> tB100(100)
        int x;
        cin>>x;
        if(tB100(x)) //if(tooBig(x,100))과 같다.
           ...
  • 미리 정의된 펑크터
    • STL은 몇 가지 기본적인 펑크터를 정의한다. 예를들어 transform()을 생각해보자
      • const int LIM = 5;
        double arr1[LIM] = {36, 39, 42, 45, 48};
        vector<double> gr8(arr,arr1 + LIM);
        ostream_iterator<double, char> out(cout," ");
        transform(gr8.begin(), gr8.end(), out, sqrt);
      • 이 코드는 각 원소의 제곱근을 계산하고, 그 결과로 얻어지는 값들을 출력 스트림으로 보낸다. 목적지 이터레이터가 오리지널 범위 안에 있을 수도 있다. 예를 들어 이 예제에 있는 out을 gr8.begin()으로 바꾸면, 새로운 값들이 이 전의 값 위에 쓰여진다. 이때 사용되는 펑크터는 하나의 전달인자로 동작하는 것이어야 한다.
      • transform(gr8.begin(), gr8.end(), m8, begin(), out, mean);
      • 이 코드는 두 개의 전달인자를 취하는 함수를 하용한다. mean(double, double) 이 구 값의 평균을 리턴한다.
      • functional 헤더파일을 이용할 수있다.
      • #include <functional>
        ...
        plus<double> add;
        double y = add(2.2, 3.4); //plus<double>::operator()()를 사용
        그러나 그것은 함수 객체를 전달인자로 제공하기 쉽게 만드다
        transform(gr8.begin(), gr8.end(), m8.begin(), out, plus<double>());
      • binder함수

//funadap.cpp -- 함수 어댑터를 사용한다.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>


void Show(double);
const int LIM = 5;

int main()
{
using namespace std;
double arr1[LIM] = { 36, 39, 42, 45, 48};
double arr2[LIM] = { 25, 27, 29, 31, 33};
vector<double> gr8(arr1, arr1 + LIM);
vector<double> m8(arr2, arr2 + LIM);
cout.setf(ios_base::fixed);
cout.precision(1);
cout<<"gr8:\t";
for_each(gr8.begin(), gr8.end(), Show);
cout<<endl;
cout<<"m8:\t";
for_each(m8.begin(), m8.end(), Show);
cout<<endl;

vector<double> sum(LIM);
transform(gr8.begin(), gr8.end(), m8.begin(), sum.begin(), plus<double>());
cout<<"sum:\t";
for_each(sum.begin(), sum.end(), Show);
cout<<endl;

vector<double> prod(LIM);
transform(gr8.begin(), gr8.end(),prod.begin(),bind1st(multiplies<double>(),2.5));
cout<<"prod:\t";
for_each(prod.begin(),prod.end(),Show);
cout<<endl;
cin.get();
return 0;
}

void Show(double v)
{
std::cout.width(6);
std::cout<<v<<' ';
}


  • STL과 string 클래스
    • next_permutation 알고리즘은 어던 범위의 내용을 그 다음 치환으로 변환한다. 문자열인 경우에, 치환들은 알파벳 또는 가나다 오름차순으로 배치된다.



//strgstl.cpp -- STL을 string에 적용한다.
#include <iostream>
#include <string>
#include <algorithm>

int main()
{
using namespace std;

string letters;

cout<<"글자 그룹을 입력하십시오(끝내려면 quit):";
while(cin>>letters && letters != "quit")
{
cout<<letters<<"의 모든 치환들 : "<<endl;
sort(letters.begin(), letters.end());
cout<<letters<<endl;
while(next_permutation(letters.begin(),letters.end()))
cout<<letters<<endl;
cout<<"다음 시퀀스를 입력하십시오(끝내려면 quit) :";
}
cout<<"프로그램을 종료합니다.";
return 0;

}






//단어 빈도수 검사 프로그램
//usealgo.cpp -- 몇가지 STL 요소들을 사용한다.
#pragma warning (diable : 4244)
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <iterator>
#include <algorithm>
#include <cctype>
using namespace std;

char toLower(char ch) { return toLower(ch);}
string & ToLower(string & st);
void display(const string & s);

int main()
{
vector<string> words;
cout<<"단어들을 입력하십시오(끝내려면 quit):\n";
string input;
while(cin>>input && input != "quit")
words.push_back(input);

cout<<"다음과 같은 단어들을 입력하셨습니다.";
for_each(words.begin(), words.end(), display);
cout << endl;

//단어들을 소문자로 변환하여 집합에 넣는다.
set<string> wordset;
transform(words.begin(), words.end(), insert_iterator<set<string>>(wordset, wordset.begin()),ToLower);
cout<<"\n단어들의 알파벳순 리스트: \n";
for_each(wordset.begin(), wordset.end(), display);
cout<<endl;

//단어와 빈도를 맵에 넣는다.
map<string, int> wordmap;
set<string>::iterator si;
for(si = wordset.begin(), si != wordset.end(), si++)
{
wordmap[*si] = count(words.begin(),words.end(), *si);/
}

//맵의 내용을 출력한다.
cout<<"\n단어별 빈도:\n";
for(si = wordset.begin(); si!=wordset.end(); si++)
cout<<*si<<": "<<wordmap[*si]<<endl;
return 0;
}
string & ToLower(string & st)
{
transform(st.begin(), st.end(), st.begin(), toLower);
return st;
}

void display(const string & s)
{
cout<<s<<" ";
}