Effective STL 學習筆記 Item 21:Comparison Function 相關

1 Always have comparison functions return false for equal values

執行下面的代碼: css

set<int, less_equal<int> >s; // s is sorted by "<="
s.insert(10);                // insert the value 10a.
s.insert(10);                // Insert the value 10b again...

第三行會成功麼?這裏咱們使用了 less_equal 做爲計算是否 Equivalence 的比較算法,結合前面 對於 Equivalence 的定義,咱們能夠知道: html

\begin{equation} \left.\begin{matrix} & !(10_a <= 10_b) \\ & !(10_b <= 10_a) \end{matrix}\right\}\Rightarrow !true \& !true \Rightarrow false \& false \Rightarrow false \end{equation}

最後的結論是先插入的 10 和後插入的 10 不是等價的 (Equivalence) ,最後會致使 10 在這個 set 裏面出現兩次。 java

一樣的問題即使是在 multiset 裏面也存在。 Multiset 確實容許一個元素在 set 裏面出現屢次,但如調用該容器的 equal_range 方法,該方法會對容器的每一個元素調用上面的公式,最後發現相同的值並不等價,從而不能返回完整的 range。 c++

讓比較函數在比較等值對象的時候,始終返回 false 則解決了上面的問題,假設: 算法

\(pred(10a, 10b) = pred(10b, 10a) =false\) ,則: sql

\begin{equation} \left.\begin{matrix} & !pred(10_a, 10_b) \\ & !pred(10_b, 10_a) \end{matrix}\right\}\Rightarrow !false \& !false \Rightarrow true \& true \Rightarrow true \end{equation}

對於 set 和 multiset,不管是插入仍是 equal_range 都沒有問題了,記住,給定兩個對象,比較函數: \(pred(a, b)\) 決定了對象 a 是否應該插在對象 b 的前面 。 對象 A 永遠不該該插在它本身( 或者其等值對象以前),要永遠返回 False 。 bash

2 Strict Weak Ordering

STL 容器中的 \(operator <()\) 要知足 Strict Weak Ordering ,直譯爲「嚴格弱排序 」,名字比較詭異,但其實就是一個受到若干「 嚴格 」約束的 < 操做符,該操做符能夠用於集合的排序,造成一個符合 STL 要求的集合。 這些約束條件包括: less

  • 反自反性 (irreflexive): 對給定的元素 x,始終知足: \(!(x < x)\)
    返回去看前面的內容,即要求 \(pred(x, x) = false\) ,要求排序算法對等值對象始終返回 false,其實也正事要求符合反自反性。
  • 非對稱性 (asymmetry): 即 $ (x < y)⇒ !(y < x)$
  • 傳遞性 (Transitivity): \((x < y) \& (y < z) \Rightarrow (x < z)\)
  • 不可比較的傳遞性: 對於給定的 x, y, z,若是 x 不能和 y 比較,y 不能和 z 比較,則 x 不能和 z 比較。

上面的這個定義不是太好記憶,下面這個衍化版卻是好記: 函數

  • Strict: pred (X, X) is always false.
  • Weak: If !pred (X, Y) && !pred (Y, X), X==Y.
  • Ordering: If pred (X, Y) && pred (Y, Z), then pred (X, Z).

若是不符合上面的規定,則對兩個對象執行 Equivalence 斷定會出問題, Associative Container 的定義也就不徹底。 post

例如,有個表示屏幕座標點的類,爲其寫 \(operator<\) 時常常犯的一個錯誤就是:

class Pos
{
public:
    Pos(int x, int y)
            :m_x(x),
             m_y(y)
    {}
    virtual ~Pos(){}
    bool operator< (const Pos& p) const
    {
        return m_x < p.m_x && m_y < p.m_y;
    }
private:
    int m_x;
    int m_y;
};

上面的 \(operator <\) 中,能夠發現 \(pred((0, 1), (1, 0)) ==false\) 且: \(pred((1, 0), (0, 1)) ==false\) , 根據定義,則 (1, 0) == (0, 1), 這明顯是一個錯誤的結論。

正確的寫法應該是:

class Pos
{
public:
    Pos(int x, int y)
            :m_x(x),
             m_y(y)
    {}
    virtual ~Pos(){}
    bool operator< (const Pos& p) const
    {
        if (m_x < p.m_x)
        {
            return true;
        }
        else if (m_x > p.m_x)
        {
            return false;
        }

        if (m_y < p.m_y)
        {
            return true;
        }
        else if (m_y > p.m_y)
        {
            return false;
        }
        return false;
    }
private:
    int m_x;
    int m_y;
};
相關文章
相關標籤/搜索