[STL] 원소를 수정하지 않는 알고리즘 (2)
in STL
for_each(b,e,f)
순차열의 모든 원소에 사용자 정의 동작을 하게 하려면 일반적으로 for_each()를 사용하는데 for_each()는 원소를 수정하지 않는 알고리즘과 원소를 수정하는 알고리즘 두 분류에 모두 포함된다. for_each(b,e,f)는 구간 [b,e)의 반복자가 p라면 f(*p)를 호출한다.
우선 예시 코드를 봐보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void Print(int n) {
cout << n << " ";
}
void main() {
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
v1.push_back(50);
//v1의 모습 10 20 30 40 50
for_each(v1.begin(), v1.begin() + 2, Print);
cout << endl;
for_each(v1.begin(), v1.begin() + 4, Print);
cout << endl;
// 구간 [v1.begin(),v1.end()) 의 원소 출력
for_each(v1.begin(), v1.end(), Print);
cout << endl;
}
근데 여기서 사용자 정책을 반영하려고 for_each() 알고리즘의 함수류를 함수가 아닌 함수객체로 바꾸면 더 다양한 출력 패턴을 구현할 수 있다. 일반 함수는 부가적인 정보를 전달할 수 없지만 함수 객체는 상태 변수를 통해서 부가적인 정보를 전달할 수 있기 때문이다. 코드를 봐보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class PrintFunctor {
private:
char fmt;
public:
explicit PrintFunctor(char c = ' ') : fmt(c) {} //생성자
void operator ()(int n) const {
cout << n << fmt;
}
};
void main() {
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
v1.push_back(50);
//v1의 모습 10 20 30 40 50
for_each(v1.begin(), v1.end(), PrintFunctor()); //원소 구분을 ' '
cout << endl;
for_each(v1.begin(), v1.end(), PrintFunctor(',')); //원소 구분을 ','
cout << endl;
for_each(v1.begin(), v1.end(), PrintFunctor('\n')); //원소 구분을 '\n'
cout << endl;
}
explicit : 컴파일러가 임의로 형변환을 하지 못하도록 막아준다. 위의 결과에서 알 수 있듯이 생성자의 매개변수에 인자를 넘김으로 추가적인 정보를 넘겨서 동작하게 할 수 있다.
lexicographical_ compare(b,e,b2,e2,f)
문자열 비교처럼 순차열의 사전순 비교 (less : <) 가 필요하다면 사용하는 알고리즘이다. 기본 비교 기준으로 less를 사용하기에 f자리에 조건자를 추가해주지 않으면 less기준으로 비교한다.
lexicographical_ compare(b,e,b2,e2)는 구간 [b,e)와 구간 [b2,e2)의 모든 원소를 문자열처럼 비교(less)하여 참과 거짓을 반환한다. 예시를 보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class PrintFunctor {
private:
char fmt;
public:
explicit PrintFunctor(char c = ' ') : fmt(c) {} //생성자
void operator ()(int n) const {
cout << n << fmt;
}
};
void main() {
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
//v1의 모습 10 20 30
vector<int> v2;
v2.push_back(10);
v2.push_back(20);
v2.push_back(50);
//v2의 모습 10 20 50
vector<int> v3;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
//v3의 모습 10 20 30
if (lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end())) {
cout << "v1 < v2" << endl;
}
else {
cout << "v1 >= v2" << endl;
}
if (lexicographical_compare(v1.begin(), v1.end(), v3.begin(), v3.end())) {
cout << "v1 < v3" << endl;
}
else {
cout << "v1 >= v3" << endl;
}
}
lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end())의 뜻이 v1<v2인데 10,20은 같지만 30<50이기때문에 참이 된다.
lexicographical_compare(v1.begin(), v1.end(), v3.begin(), v3.end())의 뜻이 v1<v3인데 모든 원소가 같기 때문에 거짓이 된다.
만약, 순차열을 비교할 때 사용자가 직접 비교 기준을 설정하려면 함수의 마지막 인자에 조건자를 추가해주면 된다. 예시를 보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template <typename T>
class Less {
public:
bool operator() (const T& left, const T& right) const {
return left < right;
}
};
void main() {
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
//v1의 모습 10 20 30
vector<int> v2;
v2.push_back(10);
v2.push_back(20);
v2.push_back(50);
//v2의 모습 10 20 50
if (lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end(), less<int>())) {
cout << "기준 less v1과 v2의 비교: true" << endl;
}
else {
cout << "기준 less v1과 v2의 비교: false" << endl;
}
if (lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end(),greater<int>())) {
cout << "기준 greater v1과 v2의 비교: true" << endl;
}
else {
cout << "기준 greater v1과 v2의 비교: false" << endl;
}
if (lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end(), Less<int>())) {
cout << "사용자 기준 Less v1과 v2의 비교: true" << endl;
}
else {
cout << "사용자 기준 Less v1과 v2의 비교: false" << endl;
}
}
위와 같이 조건자를 추가하여 다르게 동작하게 만들 수 있다.
max(a,b), max(a,b,f), min(a,b), min(a,b,f)
위 함수를 사용하면 간단하게 값을 비교하여 크거나 작은 값을 구할 수 있다.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Point {
private:
int x, y;
public:
Point() {}
explicit Point(int _x, int _y) : x(_x), y(_y) {}
int GetX() const {
return x;
}
int GetY() const {
return y;
}
void Print() const {
cout << '(' << x << ',' << y << ')' << endl;
}
};
bool XCompare(const Point& left, const Point& right) {
return left.GetX() < right.GetX();
}
bool YCompare(const Point& left, const Point& right) {
return left.GetY() < right.GetY();
}
void main() {
int a = 10, b = 20;
int r;
r=max(a, b);
cout << "max : " << r << endl;
r = min(a, b);
cout << "min : " << r << endl;
Point pt1(5, 8), pt2(3, 9);
Point pt3;
pt3 = max(pt1, pt2, XCompare);
cout << "x max : "; pt3.Print();
pt3 = max(pt1, pt2, YCompare);
cout << "y max : "; pt3.Print();
}
max(a,b)와 min(a,b)는 직관적이므로 설명을 생략한다. pt1과 pt2는 Point객체로 비교 연산자가 없어 크기 비교가 불가능 하다. 그래서 XCompare라는 이항 조건자를 사용하여 Point 객체의 x값으로 크기를 비교한다.
max_element(b,e) , max_element(b,e,f) , min_element(b,e) , min_element(b,e,f)
위 알고리즘은 구간 [b,e)에서 가장 큰 원소의 반복자, 가장 작은 원소의 반복자를 반환한다. 예시를 보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void main() {
vector<int> v;
v.push_back(30);
v.push_back(10);
v.push_back(20);
v.push_back(50);
v.push_back(40);
// v의 모습 30 10 20 50 40
vector<int>::iterator iter;
iter = max_element(v.begin(), v.end());
cout << *iter << endl;
iter = min_element(v.begin(), v.end());
cout << *iter << endl;
}
직관적이므로 설명은 생략한다.
만약 조건자버전이 필요하면 사용자 정의 조건자를 만들고 마지막 인자로 넣어주면 된다.
mismatch(b,e,b2) , mismatch(b,e,b2,f)
mismatch()는 두 순차열의 원소를 하나씩 비교하여 원소의 값이 서로 다른 첫 원소의 반복자쌍을 반환한다. 예시를 보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void main() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
// v의 모습 10 20 30 40 50
vector<int> v2;
v2.push_back(10);
v2.push_back(20);
v2.push_back(30);
v2.push_back(80);
v2.push_back(90);
// v2의 모습 10 20 30 80 90
pair<vector<int>::iterator, vector<int>::iterator> iter_pair;
iter_pair = mismatch(v.begin(), v.end(), v2.begin());
cout << "v: " << *iter_pair.first << ", " << "v2: " << *iter_pair.second << endl;
}
직관적이므로 설명은 넘어간다. 다음은 조건자 버전 mismatch()다. 여기서 주의 할 점은 조건이 참인 첫 원소의 반복자 쌍을 반환하는 것이 아닌 조건이 거짓인 첫 원소의 반복자 쌍을 반환한다. 예시를 보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool Pred(int left, int right) {
return abs(right - left) <= 5;
}
void main() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
// v의 모습 10 20 30 40 50
vector<int> v2;
v2.push_back(11);
v2.push_back(25);
v2.push_back(33);
v2.push_back(46);
v2.push_back(50);
// v2의 모습 11 25 33 46 50
pair<vector<int>::iterator, vector<int>::iterator> iter_pair;
iter_pair = mismatch(v.begin(), v.end(), v2.begin(),Pred);
cout << "v: " << *iter_pair.first << ", " << "v2: " << *iter_pair.second << endl;
}
결과에서 보이듯이 조건은 두 순차열 각 원소의 차가 5보다 작거나 같다인데 출력 결과를보면 각 원소의 차가 5보다 큰 위치를 반환한다.
search()
search()는 find_end()와 개념은 똑같다. 하지만 다른점은 search()는 일치하는 순차여링 여러개라면 search()는 첫 번째 순차열의 시작 반복자를 반환한다. 예시를 보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void main() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
v.push_back(60);
v.push_back(70);
v.push_back(30);
v.push_back(40);
v.push_back(50);
// v의 모습 10 20 30 40 50 60 70 30 40 50
vector<int> v2;
v2.push_back(30);
v2.push_back(40);
v2.push_back(50);
vector<int>::iterator iter;
iter = search(v.begin(), v.end(), v2.begin(), v2.end());
if (iter != v.end()) {
// 일치하는 첫 번째 순차열의 첫 원소의 반복자 iter
cout << "iter : " << *iter << endl; //30
cout << "iter-1 : " << *(iter-1) << endl; //20
cout << "iter+1 : " << *(iter + 1) << endl; //40
}
}
그림으로 설명하면 아래와 같다.
조건자버전 search()는 조건자버전 find_end()과 사용방법이 같으므로 생략한다.
search_n(b,e,n,x)
search_n(b,e,n,x)는 구간 [b,e)에서 원소 x가 n번 연속하는 첫 원소의 반복자를 반환한다. 예시를 보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void main() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(30);
v.push_back(30);
v.push_back(40);
v.push_back(50);
// v의 모습 10 20 30 30 30 40 50
vector<int>::iterator iter;
iter = search_n(v.begin(), v.end(),3 ,30);
if (iter != v.end()) {
cout << "iter : " << *iter << endl; //30
cout << "iter-1 : " << *(iter-1) << endl; //20
cout << "iter+1 : " << *(iter + 1) << endl; //30
}
}
직관적이므로 설명은 생략한다.
조건자 버전 search_n(b,e,n,x,f)
search_n(b,e,n,x,f)는 구간 [b,e)의 반복자가 p라면 조건자 f(*p,x)가 참인 값이 n개 연속한 첫 원소의 반복자를 반환한다. 예시를 보자.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool Pred(int left, int right) {
return abs(right - left) <= 5;
}
void main() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(32);
v.push_back(28);
v.push_back(33);
v.push_back(40);
v.push_back(50);
// v의 모습 10 20 32 28 33 40 50
vector<int>::iterator iter;
iter = search_n(v.begin(), v.end(),3 ,30,Pred);
if (iter != v.end()) {
cout << "iter : " << *iter << endl; //32
cout << "iter-1 : " << *(iter-1) << endl; //20
cout << "iter+1 : " << *(iter + 1) << endl; //38
}
}
30과의 차가 5이하인 원소가 3번 이상 연속한 첫 원소의 반복자를 반환한다. 그림으로 설명하겠다.