42-1-라.equal

equal 함수는 두 개의 반복자 구간을 비교하여 두 구간이 완전히 일치하는지 아닌지를 검사한다.

 

bool equal(InIt1 first1, InIt1 last1, InIt2 first2 [, BinPred F]);

 

first1 ~ last1 사이의 구간과 first2 이후의 구간에 있는 요소들을 일대일로 비교해 보고 모든 요소가 일치하면 true를 리턴하고 하나라도 틀리면 false를 리턴한다. 두 번째 구간은 시작 위치를 지정하는 반복자만 전달되고 끝 반복자는 전달되지 않는데 두 번째 구간도 첫 번째 구간과 길이가 같다고 가정한다. 구간끼리 비교할 때는 어차피 같은 크기의 구간을 비교하는 경우가 압도적으로 많으므로 이 가정에는 별 무리가 없다.

두 개의 똑같은 구간을 전달받는 함수들은 보통 두 번째 구간의 길이를 전달받는 인수가 따로 없고 첫 번째 구간의 길이를 사용한다. 두 반복자 구간은 반드시 같은 컨테이너에 소속될 필요는 없으며 컨테이너의 타입이 달라도 상관없다. 즉, 벡터와 리스트끼리도 비교 가능하다는 얘기인데 함수 원형에 보다시피 InIt1, Init2 등으로 반복자 타입이 다를 수 있다는 것이 표기되어 있다. 어떤 컨테이너의 반복자이든지 입력만 가능하면 구간 비교 연산을 수행할 수 있다.

반복자 구간의 대응되는 요소끼리 비교할 때 디폴트로 == 연산자를 사용하도록 되어 있어 동일성 비교를 수행하는데 특별한 비교 방식을 사용하고 싶다면 이항 조건자 F를 제공한다. 이 함수 객체는 대응되는 두 요소를 인수로 전달받아 원하는 방법으로 비교한 후 두 요소가 같으면 true를 리턴한다. 객체의 일부 멤버만 비교할 수도 있고 어느 정도의 오차를 무시할 수도 있다.

 

: equal

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

 

void main()

{

     int ari[]={8,9,0,6,2,9,9};

     vector<int> vi(&ari[0],&ari[7]);

 

     if (equal(&ari[0],&ari[7],vi.begin())) {

          puts("두 구간은 동일하다");

     } else {

          puts("두 구간은 틀리다.");

     }

}

 

정수 배열로부터 정수 벡터를 만들고 이 두 컨테이너가 동일한지 비교했으므로 비교 결과는 당연히 같은 것으로 나올 것이다. 벡터 초기화 후에 ari[5]=99; 대입문으로 배열의 요소 하나를 변경해 놓고 비교하면 틀리다는 결과가 출력된다. equal은 전체가 같아야만 true를 리턴하며 단 하나라도 틀리면 결과는 false이다. 함수 객체를 지정하면 두 요소가 같다는 조건을 마음대로 변경할 수 있다.

 

: equal2

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

 

bool compare(double a,double b)

{

     return ((int)a == (int)b);

}

 

void main()

{

     double af1[]={ 45.34, 77.84, 96.22, 91.04, 85.24 };

     double af2[]={ 45.99, 77.25, 96.86, 91.23, 86.13 };

 

     if (equal(&af1[0],&af1[4],&af2[0],compare)) {

          puts("지정 구간의 정수부가 모두 같다.");

     } else {

          puts("지정 구간의 정수부 중 일부가 일치하지 않는다.");

     }

}

 

두 개의 실수 배열을 비교하되 정수부만 비교했다. 소수부는 별로 중요하지 않을 경우 정수부만 비교하도록 비교 방법을 함수 객체로 지정했다. af1의 첫 요소를 44.44로 변경한 후 실행해 보면 틀리다는 보고를 할 것이다. 또한 0 ~ 4까지의 구간만 비교했는데 구간을 0 ~ 5로 확대하면 이때도 틀린 것으로 판단한다.

mismatch 함수는 equal의 반대 함수인데 두 반복자 구간 중 최초로 틀린 부분이 어디인가를 찾는다. equal은 같다, 다르다만 조사하는데 비해 이 함수는 틀리다면 어디쯤이 틀린지도 조사한다.

 

pair<InIt1, InIt2> mismatch(InIt1 first1, InIt1 last1, InIt2 first2 [,BinPred F]);

 

리턴값은 두 구간이 최초로 달라진 지점의 반복자 쌍을 pair 객체로 묶어서 리턴한다. 이 구조체의 first, last를 점검해 보면 어디가 최초로 다른 지점인지를 알 수 있다. 모든 구간이 일치한다면 last1과 first2+(last1-first1)의 쌍이 리턴되는데 first2+(last1-first1)이라는 수식이 복잡해 보이지만 말로 간단히 설명하면 두 번째 구간의 끝다음이다. 다음 두 문장이 똑같은 점검을 한다는 것은 쉽게 이해가 될 것이다.

 

equal(&ari[0],&ari[7],vi.begin())==true

mismatch(&ari[0],&ari[7],vi.begin()).first==ari[7]

 

모든 요소가 일치한다는 얘기나 최초로 틀린 요소가 구간 바깥의 끝다음이라는 것은 같은 뜻이다. 일단 간단한 예제부터 보자.

 

: mstmatch

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

 

void main()

{

     int ari[]={8,9,0,6,2,9,9};

     vector<int> vi(&ari[0],&ari[7]);

     vi[3]=7;

 

     pair<int *,vector<int>::iterator> p;

     p=mismatch(&ari[0],&ari[7],vi.begin());

     if (p.first != &ari[7]) {

          printf("%d번째 자리(%d,%d)부터 다르다.\n",

              p.first-ari,*(p.first),*(p.second));

     } else {

          puts("두 컨테이너가 일치한다.");

     }

}

 

정수 배열을 벡터로 복사한 후 3번째 요소를 다른 값으로 바꾸어 두 컨테이너가 틀린 값을 가지도록 했다. mismatch로 조사해 보면 최초로 틀려지는 지점의 반복자들이 pair로 묶여 리턴되며 이 반복자를 읽으면 어디가 어떻게 틀린지를 알 수 있다. vi[3]=7; 대입문을 주석 처리한 후 실행하면 일치한다는 결과가 리턴될 것이다.

다음 예제는 mismatch 함수를 사용하여 정답과 학생이 작성한 답안지의 각 요소를 비교하여 오답들을 검색한다.

 

: mstmatch2

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

 

void main()

{

     int answer[]={1,1,4,3,2,4,3,2,3,4,1,2,4,4,3,2,1,3,2,4};

     int paper[]= {1,1,4,3,3,4,3,1,3,4,1,2,4,4,3,4,1,3,2,2};

 

     pair<int *,int *> p;

     int i;

     for (i=0;;) {

          p=mismatch(&answer[i],&answer[20],&paper[i]);

          if (p.first == &answer[20]) break;

          printf("%d번 틀림, 정답=%d, 니가 쓴 답=%d\n",

              p.first-answer+1,*(p.first),*(p.second));

          i=p.first-answer+1;

     }

}

 

answer에는 정답이 저장되어 있고 paper에는 학생이 쓴 답안지가 들어 있다. mismatch로 이 두 배열을 비교하먼서 틀린 부분을 찾아내는데 모든 오답을 다 찾아야 하므로 루프를 돌렸다.

 

5번 틀림, 정답=2, 니가 쓴 답=3

8번 틀림, 정답=2, 니가 쓴 답=1

16번 틀림, 정답=2, 니가 쓴 답=4

20번 틀림, 정답=4, 니가 쓴 답=2

 

두 배열이 같이 진행되어야 하므로 첨자 i를 시작 반복자로 사용하며 오답이 발견되면 i는 다음 문제를 가리키도록 했다. 답이 틀렸다는 것 뿐만 아니라 왜 틀렸는지 정답과 학생이 쓴 답도 같이 출력할 수 있다.