如何避免长切换语句? C ++

时间:2011-04-03 23:13:07

标签: c++ performance switch-statement

我正在为我班上的“字典”工作。我有一个名为NumOfWordsInFile[]的int数组,其中NumOfWordsInFile[0]对应于A.txt中的单词数,NumOfWordsInFile[25]对应于Z.txt

就像现在一样,我对26种不同的字母条件进行了巨大的转换。我有一个名为AddWord(string word)的函数。 AddWord获取传递给它的单词的第一个字母,并将其插入相应的.txt文件中。现在这是问题所在。每次在A.txt中添加一个单词时,我必须将NumOfWordsInFile[0]增加1.我能想到的唯一方法是使用这些巨大的开关。我还有一个deleteWord函数,如果删除该单词,则相反递减NumOfWordsInFile[]。现在我不想要两个26箱开关,但问题是我不知道怎么做。现在我可以为删除功能做同样的事情,但我真的不希望有更多的代码行来完成。有更好的方法吗?

AddWord功能中的开关示例:

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

删除功能。

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 个答案:

答案 0 :(得分:7)

只是快速看一下,看起来你正在使用字母表中字母的位置来做事。

您可以使用以下语句替换所有switch语句:

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就像是'a',例如。

在相关的说明中,将来如果您实际上有大型switch语句,请考虑将代码封装在函数中:

switch (letter)
{
    case 'a':
      LetterA();
      break;

    case 'b':
      LetterB();
      break;

    ...
}

或者甚至更好,您可以使用多态来根据特定的派生类将C ++调度到您想要的方法:

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

请注意,动态调度确实会有轻微的性能损失,而上述实际使用它是一个非常糟糕的地方。解决方案I,RedX和其他人发布的解决方案更适合您的具体示例。

答案 1 :(得分:6)

在使用C或C ++时可能遇到的大多数实际字符编码中,'a''z'是连续的,因此您可以通过执行{{1}来获取数组索引其中(c - 'a')是您正在查看的c

答案 2 :(得分:6)

struct FileInfo {
  int NumWords;
  std::string Filename;
};

std::map<char, FileInfo> TheFiles; 

FileInfo & FI = TheFiles[letter];
// Work with FI.NumWords and FI.Filename

可替换地:

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

答案 3 :(得分:3)

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

只有在您的用例中只有英文字母时才能使用。

答案 4 :(得分:3)

Chars基本上是数字。 'a'是97,'b'是98,依此类推。 最简单的方法是简单地用numOfWordsInFile[n]替换每个numOfWordsInFile[current_char - 'a'],并且每个案例重复的整个代码可以驻留在一个函数中,如下所示:

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

有关更常见的解决方案,请阅读有关哈希映射和函数指针的信息(例如,对于每个字符,您可能希望分配不同的函数。

答案 5 :(得分:2)

C ++中的单个字符实际上只是与其ASCII值对应的数字。您可以相互减去字母以获取数值。因此,如果word[0]包含字母A,则word[0] - 'A'将为0

因此,您可以直接索引numOfWordsInFile数组,根本不需要切换:numOfWordsInFiled[word[0] - 'A']

请注意,'A' and 'a'具有不同的数值,因此如果您混合使用大写和小写,则必须执行一些额外的工作。

答案 6 :(得分:1)

如果您的文件是A.txt,请让您的数组索引为'A' - 'A'(= 0),如果文件为B.txt,则让数组索引为'B' - 'A'(= 1)等

答案 7 :(得分:1)

这取决于您想要的便携性或方式 国际化。如果你能够忽视这种可能性 第一个字母可能是重音字符,并假设 你永远不会在大型机或任何地方运行 否则使用EBCDIC,然后你可以将第一个字母转换为 一个特定的案例,并减去'a'或'A'(取决于具体情况) 从中获取索引。 C ++标准不保证 然而,这些字母是连续的,而且它们不在 EBCDIC,也不支持任何支持重音的编码 字符。至少,你必须测试一下 当然,第一个字符是一个字母。

因为处理国际化问题很困难 一般没有人使用编码,而且有些 编码是多字节的。对于单字节编码,它是 相当直接使用映射表;一张桌子 256个条目,由第一个字母索引(转换为无符号 char),它将索引返回到您的表中。对于多字节 编码,如UTF-8,问题更复杂:你可以 将UTF-8序列中的初始字符转换为int, 但你最终可能会得到大约一百万或更多的价值 不希望有一百万个条目的表(大多数都是 完全无关紧要。一个简单的解决方案可能是添加 “其他”的第27个条目。 (这也会像“文字”一样 “第二”。)

一种非常便携的方法是:

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

不要忘记将初始字符强制转换为unsigned char 在编制索引之前。