How to avoid long switch statements? C ++

I am working on a vocabulary for my class. I have an int array called NumOfWordsInFile[] , where NumOfWordsInFile[0] matches the number of words in A.txt and NumOfWordsInFile[25] matches Z.txt

As of now, I have a huge switch for 26 different letter conditions. I have a function called AddWord(string word) . AddWord receives the first letter of the word passed to it and inserts it into the corresponding .txt file. Now here is the problem. Every time a word is added to A.txt, I have to increment NumOfWordsInFile[0] by 1. The only way I can do this is to use these huge switches. I also have a deleteWord function that inversely reduces NumOfWordsInFile[] if the word is deleted. Now I do not want to have two 26 random switches, but the problem is that I do not know how to do this. Now I could just do the same for the delete function, but I really don't want to have hundreds of other lines of code. Is there a better way to do this?

An example of a switch in the AddWord function:

 case 'w': if (numOfWordsInFile[22] < maxWordsPerFile) { fout.open(fileName.data(), ios::app); fout << word << " " << endl; numOfWordsInFile[22]++; if (totalWordsInDict < maxWordsInDict) { totalWordsInDict++; } return(Dictionary::success); } else { return(Dictionary::failure); } case 'x': if (numOfWordsInFile[23] < maxWordsPerFile) { fout.open(fileName.data(),ios::app); fout << word << " " << endl; numOfWordsInFile[23]++; if (totalWordsInDict < maxWordsInDict) { totalWordsInDict++; } return(Dictionary::success); } else { return(Dictionary::failure); } 

Delete function.

 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); } 
+8
c ++ performance switch-statement
source share
8 answers

Balls are basically numbers. "a" is 97, "b" is 98, etc. The easiest way is to simply replace each numOfWordsInFile[n] with numOfWordsInFile[current_char - 'a'] , and all the code repeated for each case can be in a function, for example:

  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); } } 

For more general solutions, read about hash maps and function pointers (when, for example, for each char you might need to assign a different function.

+3
source share

Just by looking very quickly, it seems that you are using the position of the letters in the alphabet to do something.

You can replace all of your switch statements with a single expression that looks like this:

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

ActualLetter is something like "a", for example.

In a related article in the future, if you really have big switch statements, consider encapsulating code in a function:

 switch (letter) { case 'a': LetterA(); break; case 'b': LetterB(); break; ... } 

Or, even better, you can use polymorphism to send to C ++ the method you want based on a specific derived class:

 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(); } 

Just remember that dynamic dispatching has a (negligible) performance rating, and above this is a very bad place for actual use. Solution I, RedX and others published are much better suited for your specific example.

+7
source share

In most practical character encodings that you are likely to encounter when using C or C ++, 'a' to 'z' are adjacent, so you can get the index of the array just by using (c - 'a') , where c is the char you are looking at.

+6
source share
 struct FileInfo { int NumWords; std::string Filename; }; std::map<char, FileInfo> TheFiles; FileInfo & FI = TheFiles[letter]; // Work with FI.NumWords and FI.Filename 

As an alternative:

 std::vector<FileInfo> TheFiles; FileInfo & FI = TheFiles[std::tolower(Letter) - 'a']; 
+5
source share
 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 writing in your use case.

+3
source share

Single characters in C ++ are really numbers corresponding to their ASCII values. You can subtract letters from each other to get numerical values. Therefore, if word[0] contains the letter A, then word[0] - 'A' will be 0 .

So, you can directly index your numOfWordsInFile array, and you won’t need a switch at all: numOfWordsInFiled[word[0] - 'A'] .

Note that 'A' and 'a' have different numerical values, so you will have to do extra work if you mix upper and lower case.

+2
source share

If your file is A.txt, let your array index be 'A' - 'A' (= 0), if the file is B.txt, let your array index be 'B' - 'A' (= 1), etc. .

+1
source share

It depends on how portable you want to be, or how internationalized. If you can afford to ignore the possibility that the first letter may be an accented character and assume that you will never work on the mainframe or anywhere else that uses EBCDIC, then you can convert the first letter to a specific case and subtract "a" or "A "(depending on the case) get an index from it. The C ++ standard does not guarantee that letters are contiguous, nor are they in EBCDIC and in any of the encodings that support accented characters. At the very least, you will need to verify that the first character is a letter, of course.

Solving the internationalization problem is difficult because there is not one widely used encoding, and some of the encodings are multi-byte. For single-byte encodings, this is pretty straightforward before using the mapping table; a table with 256 records indexed by the first letter (cast to an unsigned char), which returns the index to your table. For multibyte encodings such as UTF-8, the problem is more complicated: you can translate the initial character in the UTF-8 sequence to int, but you can get values ​​of about a million or more, and you do not need to have a table with a million records (most of which are completely doesn't matter. One simple solution might be to add a 27th entry for “others.” (It will also catch the words “like” “Second.”)

A very convenient way to do this:

 int mappingTable[256]; std::fill_n(mappingTable, 256, 26); static char const upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ; static char const lower[] = "abcdefghijklmnopqrstuvwxyz; for (int i = 0; i < 26; ++ i) { mappingTable[upper[i]] = i; mappingTable[lower[i]] = i; } 

Just remember to specify the starting character in an unsigned char before indexing.

+1
source share

All Articles