i) a〈 a 는 성립하지 않는다 (nonreflexivity)
ii) a〈 b 이고 b〈 c 이면 a〈 c 이다 (transitivity)
for a, b, c ∈ A.
이 때 strict partial order 이기 때문에 〈 는 A의 모든 원소에 대해 비교가능할 필요는 없다. 이 상태에서〈 가 weak ordering 이 되면서 추가되는 조건, 즉
에 의해〈 는 total ordering 이 된다. 즉, A x A 의 모든 pairs (a,b) ∈ A x A 에 대하여, a〈 b 가 정의되거나, 정의되지 않을텐데, 정의되면 그것으로 끝이고, 정의되지 않으면 equivalence 인 것이다. 즉, '같다'. 따라서 strict weak ordering〈 에 의해 집합 A의 모든 원소쌍은 관계를 갖게 된다. 1
위에서 'strict' 라는 말이 쓰인 이유는, non-strict인 경우는 주로 '≤', 즉 '<' 또는 '=' 에 대하여 사용하기 때문이다. 즉 '≤'는 non-strict 이고 '<'는 strict 인 것이다. STL의 입장에서 생각해 보면 'a≤b and b≤a 이면 a==b' 이기 때문에 operator== 를 위해서라면 partial ordering 도 괜찮을 것 같은데 왜 굳이 strict weak ordering 을 했는지는 명확히 알지 못하겠다. 한 가지 생각은 operator< 를 정의하는 것은 operator<= 를 정의하는 것보다 명확하기 때문이 아닌가 한다. operator<=는 '< 또는 ='를 의미하기 때문에 정의하는 입장(프로그래머 입장)에서는 < 와 == 둘을 모두 고려하는 operator<= 보다는 < 만 고려하는 operator< 가 더 명확하고, 이것만으로도 operator== 가 저절로 정의될 수 있기 때문이다. 또한 C++의 경우 일반적으로 배열에는 동일 타입의 변수만을 집어 넣게 되는데 그 때 과연 'partial' 이나 incomparability 라는 상황이 생길 수 있을까? (파이썬이라면 모를까...) 하여튼 이 부분은 좀 난해하다.
이제 실제 코드로 살펴 보자. STL의 set 이나 map에 객체를 집어 넣기 위해서는 그 객체가 operator< 를 갖고 있어야 한다. 즉 set에 집어 넣는 객체에 대하여 operator< 를 호출할 수 있어야 한다. 왜냐 하면 성능의 최적화를 위하여 set과 map은 내부적으로 binary search를 이용하는데(표준 문서에 그것을 이용하라고 specify되어 있지는 않다고 한 것 같다), 그러기 위해서는 operator< 연산을 할 수 있어야 하기 때문이다. 이 전 글 에지(edge) 객체 구현해 보기 에서 정의한 operator< 를 살펴 보자.
bool edge::operator<(const edge& e) const {
const std::string& max1 = (_node1 > _node2 ? _node1 : _node2);
const std::string& max2 = (e._node1 > e._node2 ? e._node1 : e._node2);
if(max1 == max2){
const std::string& min1 = (_node1 < _node2 ? _node1 : _node2);
const std::string& min2 = (e._node1 < e._node2 ? e._node1 : e._node2);
return min1 < min2;
}
return max1 < max2;
}
위의 정의는 위에서 언급한 strict weak ordering 의 정의를 만족한다. 즉,
i) (a,b) < (a,b) 성립하지 않는다 (if 문 안쪽으로 들어간 후 false 를 return 한다)
ii) (a,b) < (c,d) 이고 (c,d) < (e,f) 이면 (a,b) < (e,f) 이다.
(증명) (a,b) < (c,d) 이므로 'max{a,b} < max{c,d}'(1) 이거나 'max{a,b} == max{c,d} 이고 min{a,b} < min{c,d}'(2)이다.
(c,d) < (e,f) 이므로 'max{c,d} < max{e,f}' (3)이거나 'max{c,d} == max{e,f} 이고 min{c,d} < min{e,f}'(4)이다.
(1), (3) 인 경우
max{a,b} < max{c,d} 이고 max{c,d} < max{e,f} 이므로 max{a,b} < max{e,f} 이다.
(1), (4) 인 경우
'max{a,b} < max{c,d}'이고, 'max{c,d} == max{e,f} 이고 min{c,d} < min{e,f}' 이므로, max{a,b} < max{c,d} == max{e,f} 이므로 max{a,b} < max{e,f} 이다.
(2), (3) 인 경우
'max{a,b} == max{c,d} 이고 min{a,b} < min{c,d}' 이고 'max{c,d} < max{e,f}' 이므로 max{a,b} < max{e,f} 이다.
(2), (4) 인 경우
'max{a,b} == max{c,d} 이고 min{a,b} < min{c,d}' 이고, 'max{c,d} == max{e,f} 이고 min{c,d} < min{e,f}' 이므로 max{a,b} == max{c,d} 이고 max{c,d} == max{e,f} 이다. 따라서 max{a,b} == max{e,f} 이다. 또한 min{a,b} < min{c,d} 이고 min{c,d} < min{e,f} 이므로 min{a,b} < min{c,d} < min{e,f} 이므로 min{a,b} < min{e,f} 이다. 따라서 max{a,b} == max{e,f} 이고 min{a,b} < min{e,f} 이다. QED.
위의 조건만으로도 모든 edge가 비교가 가능하기 때문에 strict weak ordering의 세 번째 조건 incomparability 가 equivalence를 의미한다는 사용되지 않는다.
만약 operator< 를 다음과 같이 정의했다고 가정해 보자.
bool edge::operator<(const edge& e) const {
const std::string& max1 = (_node1 > _node2 ? _node1 : _node2);
const std::string& max2 = (e._node1 > e._node2 ? e._node1 : e._node2);
//if(max1 == max2){
// const std::string& min1 = (_node1 < _node2 ? _node1 : _node2);
// const std::string& min2 = (e._node1 < e._node2 ? e._node1 : e._node2);
// return min1 < min2;
//}
return max1 < max2;
}
즉 'max 값이 큰 것이 크다'라고. 그러면 a > b ≠ c 에 대하여 (a,b) < (a,c) 도 false 가 return 되고 (a,c) < (a,b) 도 false 가 return 된다. 따라서 (a,b)와 (a,c)는 같은 것이 된다. 그러나 edge 를 정의할 때 이와 같은 상황을 의도하는 경우는 거의 없기 때문에 위의 정의는 제대로 동작하지 않게 된다. 물론 compile 에러도 나지 않고 run-time 에러도 나지 않는다. 단지 논리적 에러가 생겨서 결과가 우리가 의도하지 않은 값이 나올 뿐이다. 2
참고: strict weak ordering
'컴퓨터 > C++_STL' 카테고리의 다른 글
isnan과 isinf (0) | 2010.12.06 |
---|---|
동적 라이브러리 만들고 사용하기 (5) | 2010.05.25 |
에지(edge) 객체 구현해 보기 (0) | 2010.05.08 |
파일 목록 가져 오기 일반화 시키기 (0) | 2010.01.09 |
멤버 함수의 포인터를 배열에 넣는 방법 (0) | 2009.12.27 |