2009-10-21 9 views
1

J'ai des chaînes std :: contenant des nombres dans la section principale que j'ai besoin de trier. Les nombres peuvent être des entiers ou des flottants. Le tri vector<std::string> n'était pas optimal, j'ai trouvé le programme de tri naturel suivant qui était bien meilleur. J'ai toujours un petit problème avec des nombres plus petits que zéro qui ne trient pas juste. Quelqu'un at-il une suggestion à améliorer? Nous utilisons Visual Studio 2003.comment améliorer le programme de tri naturel pour les décimales?

Le programme complet suit.

TIA, Bert

#include <list> 
#include <string> 
#include <iostream> 

using namespace std; 

class MyData 
{ 
public: 
    string m_str; 
    MyData(string str) { 
     m_str = str; 
    } 

    long field1() const 
    { 
     int second = m_str.find_last_of("-"); 
     int first = m_str.find_last_of("-", second-1); 
     return atol(m_str.substr(first+1, second-first-1).c_str()); 
    } 

    long field2() const 
    { 
     return atol(m_str.substr(m_str.find_last_of("-")+1).c_str()); 
    } 

    bool operator < (const MyData& rhs) 
    { 
     if (field1() < rhs.field1()) { 
      return true; 
     } else if (field1() > rhs.field1()) { 
      return false; 
     } else { 
      return field2() < rhs.field2(); 
     } 
    } 
}; 

int main() 
{ 
    // Create list 
    list<MyData> mylist; 
    mylist.push_front(MyData("93.33")); 
    mylist.push_front(MyData("0.18")); 
    mylist.push_front(MyData("485")); 
    mylist.push_front(MyData("7601")); 
    mylist.push_front(MyData("1001")); 
    mylist.push_front(MyData("0.26")); 
    mylist.push_front(MyData("0.26")); 


    // Sort the list 
    mylist.sort(); 

    // Dump the list to check the result 
    for (list<MyData>::const_iterator elem = mylist.begin(); elem != mylist.end(); ++elem) 
    { 
     cout << (*elem).m_str << endl; 
    } 

    return 1; 
} 

GOT:

0.26 
0.26 
0.18 
93.33 
485 
1001 
7601 

ATTENDUS:

0.18 
0.26 
0.26 
93.33 
485 
1001 
7601 

Répondre

1

Si elle est flotter juste des cordes, je préfère suggère de créer une table avec deux colonnes (la première ligne contient la chaîne d'origine, la deuxième ligne est remplie avec la chaîne convertie pour flotter) , triez-le par la colonne float, puis affichez/utilisez la colonne de chaîne triée.

0

Si les données sont toutes des nombres, je créerais une nouvelle classe pour contenir les données.

Il peut avoir une chaîne d'inclure les données, mais vous permet d'avoir de meilleures méthodes pour modéliser le comportement - dans ce cas espacially pour mettre en œuvre l'opérateur <

La mise en œuvre pourrait également inclure l'utilisation d'une bibliothèque qui calcule à exacte précion par exemple GNU multiple precision ce serait faire la comparaison et canversion de chaîne (ou si les chiffres n'ont que beaucoup de chiffres significatifs que vous pouvez utiliser en double)

2

Utilisation atof() au lieu de atol() avoir la comparaison prendre la partie décimale du nombre en compte. Vous devrez également changer les types de retour en doubles.

+0

Brillant, cela fonctionne. – Quadmore

0

Je voudrais calculer les valeurs une fois et les stocker. Comme ils ne font pas partie de l'état des objets (ce ne sont que des valeurs calculées), marquez-les comme mutables. Ensuite, ils peuvent également être définis pendant les méthodes const.

Notez également que MyClass est un ami de lui-même et peut donc accéder aux membres privés d'un autre objet de la même classe. Donc, il n'y a pas besoin de méthodes d'accès extraterrestres. Rappelez-vous que les méthodes Accessor sont destinées à protéger les autres classes des modifications de l'implémentation et non de la classe que vous implémentez. Le problème avec la commande est que atoi() ne fait que lire l'entier (c'est-à-dire qu'il s'arrête au caractère '.') Donc tous vos nombres inférieurs à 0 ont une valeur nulle pour la comparaison et ils apparaîtront donc Pour comparer avec la valeur complète, vous devez les extraire en tant que valeur en virgule flottante (double)

class MyData 
{ 
private: 
    mutable  bool gotPos; 
    mutable  double f1; 
    mutable  double f2; 
public: 
    /* 
    * Why is this public? 
    */ 
    std::string m_str; 

    MyData(std::string str) 
     :gotPos(false) 
     ,m_str(str)  // Use initializer list 
    { 
     // If you are always going to build f1,f2 then call BuildPos() 
     // here and then you don't need the test in the operator < 
    } 

    bool operator < (const MyData& rhs) 
    { 
     if (!gotPos) 
     { buildPos(); 
     } 
     if (!rhs.gotPos) 
     { rhs.buildPos(); 
     } 
     if (f1 < rhs.f1) return true; 
     if (f1 > rhs.f1) return false; 
     return f2 < rhs.f2; 
    } 
    private: 
     void buildPos() const 
     { 
      int second = m_str.find_last_of("-"); 
      int first = m_str.find_last_of("-", second-1); 

      // Use boost lexical cast as it handles doubles 
      // As well as integers. 
      f1 = boost::lexical_cast<double>(m_str.substr(first + 1, second-first - 1)); 
      f2 = boost::lexical_cast<double>(m_str.substr(second + 1)); 
      gotPos = true; 
     } 
}; 
Questions connexes