C++ 如何有效地识别字符串命令?
给定一系列命令和必须为每个命令运行的非常独特的代码:C++ 如何有效地识别字符串命令?,c++,string,performance,hash,C++,String,Performance,Hash,给定一系列命令和必须为每个命令运行的非常独特的代码: if(cmd == "cmd.setBoosterRocket") ... else if(cmd == "cmd.windSales") ... else if(cmd == "cmd.selfDustruct") ... else if(cmd == "cmd.unleashHounds") ... 如何对其进行优化?被放入s
if(cmd == "cmd.setBoosterRocket")
...
else if(cmd == "cmd.windSales")
...
else if(cmd == "cmd.selfDustruct")
...
else if(cmd == "cmd.unleashHounds")
...
如何对其进行优化?被放入switch语句中,即
我考虑制作一个散列向量:
std::hash<std::string> hasher;
for(std::string command : m_commandList)
m_mashes.push_back(hasher(command)
std::散列哈希器;
for(std::string命令:m_commandList)
m_mashes.push_back(hasher(命令)
但是向量不能作为switch case语句的一部分进行访问,因为它不是constexpr。字符串命令列表在编译时是已知的,我可以潜在地对哈希值进行硬编码……但这似乎不是一个好主意。一种可能的方法是标记化:制作一个
枚举
类型和一个字典u利用开关(以一种比硬编码哈希更为程序员和编译器友好的方式),并且只具有对数复杂性
enum Command {SET_BOOSTER_ROCKET, WINDSALES, ETC};
const std::map<std::string, Command> commands = {
{"cmd.setBoosterRocket", SET_BOOSTER_ROCKET},
{"cmd.windsales", WINDSALES},
{"othercommands", ETC},
};
如果有很多事情要开始,那么像这样标记命令可能会有点乏味,但这样做在可伸缩性和可读性之间有一个很好的平衡。好吧,为您的用例编写一个简单的哈希函数:
static constexpr hasher = [](auto&& s){ return char(s.size() < xyz ? 0 : expr); };
是的,您不能使用(mis-?)故障排除功能。如果您确实希望其中一个故障排除功能,请使用goto
现在您可以编写switch语句:
switch (hasher(cmd)) {
CASE("cmd.setBoosterRocket")
...
CASE("cmd.windSales")
...
CASE("cmd.selfDustruct")
...
CASE("cmd.unleashHounds")
...
...
}
大多数编译器都会提醒您注意重复的case语句,所以在获得廉价、唯一和紧密绑定的哈希之前,请不要乱动它
不要忘记
#undef CASE
来限制宏的作用域。另一种方法是创建单独的函数来处理每个案例,然后创建一个映射作为“dispatcher”:
const static std::无序映射调度程序{
{“cmd.setBoosterRocket”、&setBoosterRocketHandler},
{“cmd.windSales”、&windSalesHandler},
{“cmd.selfDustruct”、&selfDustructHandler},
{“cmd.unreleasehounds”&sunleashHoundsHandler},
};
auto const it=dispatcher.find(cmd);
if(it==dispatcher.end()){/*处理默认情况*/}
else{(*it)(;}
使用C++20,可以使用constepr
定义索引
-lambda来提高效率
static constexpr auto commands = { "cmd.setBoosterRocket", "cmd.windSales", ... };
constexpr auto index = [&](std::string s) -> size_t { /*...*/ };
switch(quick_index(cmd))
{
case index("cmd.setBoosterRocket"):
// ...
break;
default:
// ...
break;
}
对于quick_index
,它不必是constexpr
,例如,您可以使用unordered_map
查找O(1)中字符串的索引
下面是一个使用二进制搜索的完整示例(即constexpr
,因此它既可以用于索引
,也可以用于快速索引
)
#包括 #包括 #包括 #包括 使用名称空间std; 模板 constexpr ForwardIt binary_find(ForwardIt first,ForwardIt last,const T&value,comp={}) { first=std::下限(第一个、最后一个、值、comp); 返回first!=last&&!comp(值,*first)?first:last; } int main() { constexpr auto-sorted=[](数组a){sort(a.begin(),a.end());返回a;}; static constexpr auto commands=排序({“bar”、“foo”、“foobar”}); constexpr自动索引=[&](字符串视图) {return binary_find(commands.begin(),commands.end(),s)-commands.begin();}; string_view cmd=“bar”; 开关(索引(cmd)) { 案例索引(“bar”):
。为什么?开关,不管它是可到达的还是死的。看起来你是对的。我不知道你可以启动一个<代码>开关>代码>除了一个案例。如果你想看到一些疯狂的东西,那么你可以用C和C++中的开关来做,看看哇。那太棒了。
无法将所有可能性都转换为将映射到执行所需操作的。如果您不关心错误检查,则会将整个shebang简化为std::string
。但是你真的应该关心错误检查。它不会添加太多额外的代码,并且使调试更容易。你可以用成对的排序数组实现编译时常量映射的非常好的近似值。然后你可以mymap[cmd]()
在该数组中搜索命令。@FrançoisAndrieux不幸的是,d不要做你想做的事情。这对C++来说太简单了!你想要的。我们有多少个字符串?“你是对的。每次我都能得到它,这不是我第一次犯那个错误。在宏中,<代码>断裂;< /Code >会在第一个代码>案例中引起麻烦:std::binary_search
switch (hasher(cmd)) { CASE("cmd.setBoosterRocket") ... CASE("cmd.windSales") ... CASE("cmd.selfDustruct") ... CASE("cmd.unleashHounds") ... ... }
const static std::unordered_map<std::string, void(*)()> dispatcher{ { "cmd.setBoosterRocket", &setBoosterRocketHandler }, { "cmd.windSales", &windSalesHandler }, { "cmd.selfDustruct", &selfDustructHandler }, { "cmd.unleashHounds", &sunleashHoundsHandler }, }; auto const it = dispatcher.find(cmd); if (it == dispatcher.end()) { /* handle default case */ } else { (*it)(); }
static constexpr auto commands = { "cmd.setBoosterRocket", "cmd.windSales", ... }; constexpr auto index = [&](std::string s) -> size_t { /*...*/ }; switch(quick_index(cmd)) { case index("cmd.setBoosterRocket"): // ... break; default: // ... break; }
#include <algorithm> #include <array> #include <iostream> #include <string_view> using namespace std; template<class ForwardIt, class T, class Compare = std::less<>> constexpr ForwardIt binary_find(ForwardIt first, ForwardIt last, const T& value, Compare comp = {}) { first = std::lower_bound(first, last, value, comp); return first != last && !comp(value, *first) ? first : last; } int main() { constexpr auto sorted = [](array<string_view, 3> a) {sort(a.begin(), a.end()); return a;}; static constexpr auto commands = sorted({ "bar", "foo", "foobar" }); constexpr auto index = [&](string_view s) { return binary_find(commands.begin(), commands.end(), s)-commands.begin(); }; string_view cmd = "bar"; switch(index(cmd)) { case index("bar"): cout << "bartender" << endl; break; case index("foo"): cout << "fighter" << endl; break; case index("foobar"): cout << "fighter bartender" << endl; break; default: cout << "moo" << endl; break; } }