[STL] 원소를 수정하지 않는 알고리즘 (2)



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;
} 

image

근데 여기서 사용자 정책을 반영하려고 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;
} 

image

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;
	}
} 

image

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;
	}
} 

image

위와 같이 조건자를 추가하여 다르게 동작하게 만들 수 있다.

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();
} 

image

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;
} 

image

직관적이므로 설명은 생략한다.

만약 조건자버전이 필요하면 사용자 정의 조건자를 만들고 마지막 인자로 넣어주면 된다.

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;
} 

image

직관적이므로 설명은 넘어간다. 다음은 조건자 버전 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;
} 

image

결과에서 보이듯이 조건은 두 순차열 각 원소의 차가 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
	}
} 

image

그림으로 설명하면 아래와 같다.

image

조건자버전 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
	}
} 

image

직관적이므로 설명은 생략한다.

조건자 버전 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
	}
} 

image-20220319120717006

30과의 차가 5이하인 원소가 3번 이상 연속한 첫 원소의 반복자를 반환한다. 그림으로 설명하겠다.

image




© 2022. by KSC

Powered by sora