如何避免过长的 switch 语句? C++
我正在为我的班级编写一本“词典”。我有一个名为 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);
}
I am working on a "dictionary" for my class. I have an int array called NumOfWordsInFile[]
where NumOfWordsInFile[0]
corresponds to how many words are in A.txt and NumOfWordsInFile[25]
corresponds to Z.txt
As it is now I have a huge switch for the 26 different conditions of letters. I have a function called AddWord(string word)
. AddWord gets the first letter of the word passed to it and inserts it into the appropriate .txt file. Now here is the problem. Everytime a word is added to A.txt I must increment NumOfWordsInFile[0]
by 1. The only way I can think of to do this is with these huge switches. I also have a deleteWord function which conversely decrements NumOfWordsInFile[]
if the word is deleted. Now I dont want to have two 26 case swithes but the problem is I dont know how else to do it. Now I could just do the same thing for the delete function but I really dont want to have hundreds of more lines of code to go through. Is there a better way to do this?
Sample of the 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);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
快速浏览一下,您似乎正在使用字母表中字母的位置来执行操作。
您可以将所有 switch 语句替换为一个如下所示的语句:
例如,ActualLetter 类似于“a”。
与此相关的是,将来如果您确实有大型 switch 语句,请考虑将代码封装在函数中:
或者更好的是,您可以使用多态性让 C++ 根据特定的派生类分派到您想要的方法:
请注意,动态调度确实对性能有(轻微的)影响,而上面的情况是实际使用它的一个非常糟糕的地方。我、RedX 和其他人发布的解决方案更适合您的具体示例。
Just taking a very quick look, it seems like you're using the position of the letter in the alphabet to do stuff.
You could replace all your switch statements with one statement that looks like:
ActualLetter is something like, 'a', for example.
On a related note, in the future if you actually have large switch statements, consider encapsulating the code in functions:
Or even better, you can use polymorphism to have C++ dispatch to the method you want based on the specific derived class:
Just note, dynamic dispatch does have a (slight) performance hit, and the above is a very bad place to actually use it. The solution I, RedX, and others have posted is much better suited to your specific example.
或者:
Alternatively:
在使用 C 或 C++ 时可能遇到的大多数实际字符编码中,
'a'
到'z'
是连续的,因此您可以获得数组索引只需执行(c - 'a')
即可使用,其中c
是您正在查看的char
。In most practical character encodings that you're likely to encounter whilst using C or C++,
'a'
to'z'
are contiguous, so you can get the array index to use simply by doing(c - 'a')
, wherec
is thechar
you're looking at.字符基本上是数字。 “a”是 97,“b”是 98,依此类推。
最简单的方法是简单地将每个
numOfWordsInFile[n]
替换为numOfWordsInFile[current_char - 'a']
并且每种情况重复的整个代码可能驻留在一个函数中,例如this:对于更通用的解决方案,请阅读哈希映射和函数指针(例如,当您可能想要为每个字符分配不同的函数时)。
Chars are basically numbers. 'a' is 97, 'b' is 98 and so on.
The easiest way is to simply replace every
numOfWordsInFile[n]
withnumOfWordsInFile[current_char - 'a']
and the whole code repeated for each case may reside in a function, like this:For more general solutions read about hash maps and function pointers (when, for instance, for each char you might want to assign a different function.
仅当您的用例中只有英文字母时,这才有效。
This will only work if you only have english letter in your use-case.
C++ 中的单个字符实际上只是与其 ASCII 值相对应的数字。您可以将字母相减以获得数值。因此,如果
word[0]
包含字母 A,则word[0] - 'A'
将是0
。因此,您可以直接索引
numOfWordsInFile
数组,根本不需要开关:numOfWordsInFiled[word[0] - 'A']
。请注意,
'A' 和 'a'
具有不同的数值,因此如果您混合使用大小写,则必须做一些额外的工作。Single characters in C++ are really just numbers corresponding to their ASCII values. You can subtract letters from each other to get numerical values. So if
word[0]
contains the letter A, thenword[0] - 'A'
will be0
.So you can index your
numOfWordsInFile
array directly, and you won't need a switch at all:numOfWordsInFiled[word[0] - 'A']
.Note that
'A' and 'a'
have different numeric values, so you'll have to do some extra work if you're mixing upper and lower case.如果您的文件是 A.txt,则数组索引为
'A' - 'A'
(= 0),如果文件是 B.txt,则数组索引为'B ' - 'A'
(= 1) 等If your file is A.txt, let your array index be
'A' - 'A'
(= 0), if the file is B.txt, let the array index be'B' - 'A'
(= 1), etc.这取决于您想要的便携程度,或者如何
国际化。如果你有能力忽略这种可能性
第一个字母可能是重音字符,并假设
你永远不会在大型机或任何地方运行
else 使用 EBCDIC,那么您可以将第一个字母转换为
具体情况,减去“a”或“A”(取决于具体情况)
从中获取索引。 C++ 标准不保证
然而这些字母是连续的,而且它们不在
EBCDIC,也不在任何支持重音的编码中
人物。至少,您必须测试
当然,第一个字符是一个字母。
处理国际化问题很困难,因为
没有一种通用的编码方式,并且有一些
编码是多字节的。对于单字节编码,它是
相当直接地使用映射表;一张桌子
256 个条目,按第一个字母索引(转换为无符号
char),它将索引返回到表中。对于多字节
编码,如 UTF-8,问题更复杂:您可以
将 UTF-8 序列中的初始字符转换为 int,
但你最终可能会得到大约一百万或更多的值,并且你
不想要一个包含一百万个条目的表(其中大部分是
完全无关。一种简单的解决方案可能是添加
第 27 个条目为“其他”。 (这也会捕获“单词”,例如
“2nd”。)
一种非常便携的方法是:
只是不要忘记将初始字符转换为 unsigned char
在索引之前。
It depends on how portable you want to be, or how
internationalized. If you can afford to ignore the possibility
that the first letter might be an accented character, and assume
that you're never going to have run on a 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)
from it to obtain the index. The C++ standard doesn't guarantee
that the letters are contiguous however, and they aren't in
EBCDIC, nor in any of the encodings which support accented
characters. At the very least, you'll have to test that the
first character is a letter, of course.
Handling the internationalization issue is difficult, since
there is no one generally used encoding, and some of the
encodings are multibyte. For the single byte encodings, it's
fairly straight foreward to use a mapping table; a table with
256 entries, indexed by the first letter (cast to unsigned
char), which returns the index into your table. For multibyte
encodings, like UTF-8, the issue is more complicated: you can
translate the initial character in a UTF-8 sequence to an int,
but you can end up with values around a million or more, and you
don't want a table with a million entries (most of which are
completely irrelevant. One simple solution might be to add
a 27th entry for "other". (This would also catch "words" like
"2nd".)
A very portable way to do this would be:
Just don't forget to cast the initial character to unsigned char
before indexing.