8
votes

Comment éviter les relevés de commutation longs? C ++

Je travaille sur un "dictionnaire" pour ma classe. J'ai un tableau Int appelé numofordsinfile [] code> où numotwordsinfile [0] code> correspond au nombre de mots dans A.txt et NumofordsInfile [25] code> correspond à z.txt

comme il est maintenant que j'ai maintenant un énorme commutateur pour les 26 conditions différentes de lettres. J'ai une fonction appelée addword (word de chaîne) code>. AddWord obtient la première lettre de la Parole qui lui est transmise et l'insère dans le fichier .txt approprié. Maintenant, voici le problème. Chaque fois qu'un mot est ajouté à A.txt, je dois incrémenter numotwordsinfile [0] code> par 1. La seule façon dont je peux penser à faire cela est avec ces énormes commutateurs. J'ai aussi une fonction DELETEWORD qui inverse des décréments numordsinfile [] code> si le mot est supprimé. Maintenant, je ne veux pas avoir deux 26 cas suit, mais le problème est que je ne sais pas comment le faire d'autre. Maintenant, je pouvais juste faire la même chose pour la fonction Supprimer, mais je ne veux vraiment pas avoir de centaines de lignes de code à suivre. Existe-t-il une meilleure façon de faire cela? P>

échantillon de l'interrupteur dans le AddWord code> Fonction: p>

bool Dictionary::DeleteAWord(string word)
{
    ofstream fout;
    ifstream fin;
    string x;
    string fileName="#.txt";
    int count=0;
    vector <string> words;
    bool deleted=false;

    fileName[0]=toupper(word[0]);
    fin.open(fileName.data()); //makes the file depending on the first letter of the argument "word"

    while (fin >> x)
    {
        words.push_back(x);
        count++;//number of elements in vector
    }
    if (SearchForWord(x))
    {
        for ( ;count > 0; count--)
        {
            if (words[count-1] == word)
            {
                // cout << "Found word " << word << " during search, now deleting" << endl;
                words.erase(words.begin()+(count-1));
                deleted = true;

                /*
                    This clearly doesn't work and is what I need help with, I know why it
                    doesn't work but I don't know how to make it better than having another
                    huge switch.
                */
                numOfWordsInFile[toupper(word[0])]--;
                /*

                */

                totalWordsInDict--;
                fin.close();
            }
        }

        if (deleted)
        {
            fout.open(fileName.data());
            for (int i = 0; i < words.size(); i++)
                fout << words[i] << endl;
            return(Dictionary::success);
        }
        return(Dictionary::failure);
    }
    return(Dictionary::failure);
}


1 commentaires

Vous avez votre réponse. Presque tout le monde accepte (pour une bonne raison) sur l'utilisation de la disposition contiguë des lettres A-Z pour accomplir ce que vous voulez. Tant que vous utilisez uniquement des caractères anglais (comme RedX signalé), c'est la meilleure solution.


8 Réponses :


1
votes

Si votre fichier est A.TXT, laissez votre index de tableau 'a' - 'a' (= 0), si le fichier est b.txt, laissez l'index de la matrice. > 'B' - 'A' (= 1), etc.


0 commentaires

3
votes
if(numOfWordsInFile[letter - 'A']<maxWordsPerFile){
 fout.open(fileName.data(),ios::app);
 fout<<word<<" "<<endl;
 numOfWordsInFile[letter - 'A']++;
 if(totalWordsInDict<maxWordsInDict){
   totalWordsInDict++;
 }
 return(Dictionary::success);
}else{
 return(Dictionary::failure);
}
This will only work if you only have english letter in your use-case.

0 commentaires

7
votes

Il suffit de prendre un look très rapide, il semble que vous utilisiez la position de la lettre de l'alphabet pour faire des choses.

Vous pouvez remplacer toutes vos relevés de commutation avec une instruction qui ressemble à: p>

class BaseLetter
{
   ...
public:
   virtual void DoStuff() = 0;
};

class LetterA : public BaseLetter
{
public:
   void DoStuff();
};

class LetterB : public BaseLetter
{
public:
    void DoStuff();
};

void Foo(BaseLetter *letter)
{
    // Use dynamic dispatch to figure out what to do
    letter->DoStuff();
}


2 commentaires

Et dans les cas où vous décidez d'ajouter des caractères supplémentaires (tels que les chiffres), vous devez simplement remplacer la première ligne avec une fonction qui prend un caractère et renvoie un numéro d'index.


@swetstrup: Exactement. C'est un exemple parfait de ne pas répéter vous-même.



6
votes

Dans la plupart des codages de caractères pratiques que vous êtes susceptibles de rencontrer tout en utilisant C ou C ++, 'a' à 'z' sont contigus, afin que vous puissiez obtenir le Index de tableau à utiliser simplement en effectuant (c - 'A') , où c est le char vous regardez.


3 commentaires

Il existe une probabilité sérieuse que votre compte bancaire et votre dossier d'assurance soient stockés avec un codage où les lettres d'alphabet anglaises ne sont pas contiguës (EBCDIC). Cependant, ces systèmes sont probablement programmés dans Cobol ...


@ 6502: Ce n'est pas un problème, tant que votre matrice a la taille ('Z' - 'A' + 1). Vous aurez juste quelques entrées inutilisées au milieu. Tous les codages de caractères Sains ont z > a .


@Msalters: vrai. Mon commentaire était tout simplement sur le mot "tout pratique" ... ignorer un codage dans lequel un pourcentage sérieux de l'informatique d'entreprise est fait aujourd'hui n'est pas à mon avis correct. La solution est correcte (et je n'ai pas post la post une parce que c'était présent), mais le commentaire sur les caractères codant n'est pas. Ajout probablement «sur les systèmes programmés dans C» le rendrait mieux ... Les machines EBCDIC normalement sont programmées avec d'autres langues (par exemple, ils traitent avec '{').



2
votes

Les caractères moniques en C ++ ne sont vraiment que des chiffres correspondant à leurs valeurs ASCII. Vous pouvez soustraire des lettres les unes des autres pour obtenir des valeurs numériques. Donc, si mot [0] contient la lettre A, alors mot [0] - 'a' sera 0 . .

Vous pouvez donc indexer votre Numofordsinfile Gamme directement, et vous n'aurez pas besoin d'un commutateur sur tout: NumofordsInfiled [mot [0] - 'A'] . < / p>

Notez que 'A' et 'A' a différentes valeurs numériques, vous devrez donc faire un travail supplémentaire si vous mélangez des majuscules et des minuscules.


0 commentaires

6
votes
std::vector<FileInfo> TheFiles;
FileInfo & FI = TheFiles[std::tolower(Letter) - 'a'];

2 commentaires

Enfin une réponse saine: la structure de données la plus simple pour la tâche est pas définie par la simplification de la structure de données, mais par la simplification des interactions (pour cette tâche). Favorez la représentation, concentrez-vous sur les méthodes disponibles.


@Matthieu M. je souhaite. Mais la quête d'une "efficacité" et "intelligente" semble alterner toutes les premières années de toute carrière de programmeurs.



3
votes

Les caractères sont fondamentalement des nombres. 'A' est 97, 'B' est 98 et ainsi de suite. Le moyen le plus simple est de simplement remplacer chaque numotwordsinfile [n] code> avec numotwordsinfile [actuel_char - 'a'] code> et l'ensemble du code répété pour chaque cas peut réexaminer dans une fonction, comme Ceci:

   int AddWord(char current_char) {
    if(numOfWordsInFile[current_char - 'a']<maxWordsPerFile){
     fout.open(fileName.data(),ios::app);
     fout<<word<<" "<<endl;
     numOfWordsInFile[current_char - 'a']++;
      if(totalWordsInDict<maxWordsInDict){
       totalWordsInDict++;
     }
     return(Dictionary::success);
    }else{
     return(Dictionary::failure);
    }
   }


0 commentaires

1
votes

Cela dépend de la manière dont vous voulez être portable, ou comment Internationalisé. Si vous pouvez vous permettre d'ignorer la possibilité que la première lettre pourrait être un caractère accentué et supposer que vous n'allez jamais avoir couru sur un ordinateur central ou n'importe où sinon qui utilise ebcdic, vous pouvez convertir la première lettre en un cas spécifique et soustraire 'A' ou 'A' (selon le cas) de cela pour obtenir l'index. La norme C ++ ne garantit pas que les lettres sont cependant contiguës et ils ne sont pas dans EBCDIC, ni dans aucun des codages qui soutiennent l'accentuation personnages. À tout le moins, vous devrez tester que le Le premier caractère est une lettre, bien sûr.

Traitement du problème de l'internationalisation est difficile, car il n'y a personne généralement utilisé en codage, et une partie de la Les codages sont des multibytes. Pour les codages d'octets individuels, il est assez droit vers l'avant d'utiliser une table de cartographie; une table avec 256 entrées, indexées par la première lettre (non signé Char), qui renvoie l'index dans votre table. Pour Multibyte Encodages, comme UTF-8, le problème est plus compliqué: vous pouvez Traduisez le caractère initial dans une séquence UTF-8 vers un int, Mais vous pouvez vous retrouver avec des valeurs d'environ un million ou plus, et vous ne veux pas une table avec une million d'entrées (dont la plupart sont les plus complètement non pertinent. Une solution simple peut être d'ajouter une 27ème entrée pour "Autre". (Cela attraperait également des "mots" comme "2nd".)

Un moyen très portable de le faire serait: xxx

n'oublie pas de jeter le personnage initial à un caractère non signé avant l'indexation.


0 commentaires