2010-10-30 15 views
5

Sprawdź poniższy kod:Korzystanie komparator dla STL ustawić

string toLowerCase(const string& str) { 
    string res(str); 
    int i; 

    for (i = 0; i < (int) res.size(); i++) 
     res[i] = (char) tolower(res[i]); 

    return res; 
} 

class LeagueComparator 
{ 
public: 
    bool operator()(const string& s1, const string& s2) 
    { 
     return toLowerCase(s1) < toLowerCase(s2); 
    } 
}; 

int main() 
{ 
    set<string, LeagueComparator> leagues; 
    set<string, LeagueComparator>::iterator iter; 

    leagues.insert("BLeague"); 
    leagues.insert("aLeague"); // leagues = {"aLeague", "BLeague"} 
    leagues.insert("ALeague"); 

    for (iter = leagues.begin(); iter != leagues.end(); iter++) 
     cout << *iter << endl; 

    return 0; 
} 

wyjście jest:

aLeague 
BLeague 

co jest szokujące dla mnie. Myślałem (i spodziewa) wyjście byłoby:

aLeague 
ALeague 
BLeague 

Przed wykonaniem leagues.insert("ALeague"); The leagues zawiera "aLeague" i "BLeague". Moje pytanie brzmi: podczas wykonywania leagues.insert("ALeague");, dlaczego maszyna traktuje "ALeague" == "aleague"? Zgodnie z moim rozumieniem, nie ma elementu "ALeague" w leagues. Tak więc "ALeague" należy wstawić do leagues. Komparator powinien określić, gdzie umieścić "ALeague".

Z góry dziękuję.

PS: Proszę mnie nie uderzać za używanie obsady w stylu C. : P Jestem zbyt leniwy, aby wpisać static_cast.

+0

Fakt, że czuje trzeba pracować, aby wykonać C++ styl obsada jest jednym z głównych powodów C++ odlewów typu istnieje - a mianowicie, że należy unikać jakichkolwiek odlewania w C++. W takim przypadku należy całkowicie usunąć rzuty i zamiast tego użyć odpowiednich typów. To znaczy. zamiast '(int) res.size()', usuń rzutowanie i zmień typ 'i' na 'unsigned'. –

+0

Ponadto, 'i' powinno być zadeklarowane w pętli, a nie poza pętlą. A w C++, doLowerCase powinien prawdopodobnie wywoływać 'std :: transform (str.begin(), str.end(), str.begin(), std :: ptr_fun (tolower))' zamiast pisać jawną pętlę. –

+0

@Billy ONeal: dzięki.Muszę być używane do użycia 'transform()'. że 'toLowerCase' zostało napisane przeze mnie wiele lat temu. Myślę, że nie wiedziałem o "transformacji" w tym czasie. zaktualizuję moją bazę kodów. – Donotalo

Odpowiedz

14

Twój komparator, dzięki toLowerCase, mówi, że "aLeague" == "ALeague". Ponieważ (według twojego porównania) "aLeague" < "ALeague" == false i "ALeague" < "aLeague" == false, muszą być równoważne. Wstawianie równoważnego elementu do zestawu nic nie robi.

+3

+1. Zauważ, że komparator nie ustanawia równości, ustanawia równoważność. Istnieje różnica w.r.t. dokumentacja standardowa i STL. –

+0

Dzięki, edytuję mój wpis. –

3

Biorąc pod uwagę dostarczony przez ciebie komparator, "ALeague" jest rzeczywiście równoznaczny "aLeague".

względu dwóch wartości x i y, oraz mniej niż komparatora z:

  • Jeśli z (x, y) jest prawdziwy, wówczas X jest mniejsza niż y
  • jeśli Z (Y, x) jest prawdziwe, a następnie y jest mniejsze niż x
  • Jeśli nie jest prawdą, to x jest równoważne y
  • Jeśli oba są prawdziwe, to masz uszkodzony komparator.
+0

+1, ale niewielki problem z trzecim bulletem w.r.t. Dokumenty STL. Istnieje rozróżnienie między równością a równoważnością. Mniej niż porównawczy nie może ustanowić równości, a jedynie równoważność. –

+0

@Billy ONeal: według STL doc (nie mam tego), jakie są definicje "równości" i "równoważności"? – Donotalo

+0

@Donotalo: Równość jest porównywana za pomocą porównywania równości lub 'operator =='. Równoważność to stan, w którym mniej niż porównywarek lub "operator <" zwraca wartość false dla każdego z porządków argumentów, jak tutaj podano. Konceptualnie jest to różnica między [porównywalną równością] (http://www.sgi.com/tech/stl/EqualityComparable.html) a [mniej niż porównywalną] (http://www.sgi.com/tech/stl/ LessThanComparable.html). –

4

Po wstawieniu dowolnej wartości do zestawu obiekt sprawdza, czy zawiera już tę wartość. Twój obiekt LeagueComparator porównuje ALeague z pozostałymi dwiema wartościami już w zestawie. Określa, że ​​istniejąca wartość aLeague nie jest ani większa ani mniejsza niż proponowana nowa pozycja (ALeague), więc muszą one być równe, a więc nie kontynuować wstawiania. Zestaw pozostaje tylko z dwoma elementami. Taki jest cel dostarczania obiektu porównywania klientów, dzięki czemu można kontrolować, w jaki sposób zestaw określa, czy dwa elementy pasują do siebie.

+0

+1. Zauważ, że aby zachować spójność z dokumentami STL, większość zastosowań słowa "równy" należy zastąpić "równoważnym". Mniej niż komparatorzy nie mogą ustanowić równości. –

0

zastąpić LeagueComparator z

class LeagueComparator 
{ 
public: 
    bool operator()(const string& s1, const string& s2) 
    { 
     return toLowerCase(s1) < toLowerCase(s2) || 
       !(toLowerCase(s2) < toLowerCase(s1)) && s1 < s2; 
    } 
}; 
+0

Jest to równoznaczne z brakiem porównywalnika. Twój komparator po prostu zwraca 's1

+0

To nie jest prawda. 's1 =" b "', 's2 =" A "'. Mój komparator zwraca 'false' z powodu' false || ! true && true = false'. 's1