質問
このようなコードをたくさん書いていることに気づきました:
set<int> affected_items;
while (string code = GetKeyCodeFromSomewhere())
{
if (code == "some constant" || code == "some other constant") {
affected_items.insert(some_constant_id);
} else if (code == "yet another constant" || code == "the constant I didn't mention yet") {
affected_items.insert(some_other_constant_id);
} // else if etc...
}
for (set<int>::iterator it = affected_items.begin(); it != affected_items.end(); it++)
{
switch(*it)
{
case some_constant_id:
RunSomeFunction(with, these, params);
break;
case some_other_constant_id:
RunSomeOtherFunction(with, these, other, params);
break;
// etc...
}
}
このコードを書くことになった理由は、実行する可能性のある複数のキーコードを受け取った場合でも、2番目のループで関数を1回だけ実行する必要があるからです。
これは最善の方法とは思えません。きちんとした方法はありますか?
解決
セット内の実際の値を気にする必要はないので、intの設定ビットに置き換えることができます。線形時間検索ロジックをログ時間検索ロジックに置き換えることもできます。最終的なコードは次のとおりです。
// Ahead of time you build a static map from your strings to bit values.
std::map< std::string, int > codesToValues;
codesToValues[ "some constant" ] = 1;
codesToValues[ "some other constant" ] = 1;
codesToValues[ "yet another constant" ] = 2;
codesToValues[ "the constant I didn't mention yet" ] = 2;
// When you want to do your work
int affected_items = 0;
while (string code = GetKeyCodeFromSomewhere())
affected_items |= codesToValues[ code ];
if( affected_items & 1 )
RunSomeFunction(with, these, params);
if( affected_items & 2 )
RunSomeOtherFunction(with, these, other, params);
// etc...
他のヒント
1つのアプローチは、文字列からブールへのマップを維持することです。メインロジックは次のように開始できます。
if(done[code])
continue;
done[code] = true;
その後、コードを特定するとすぐに適切なアクションを実行できます。
もう1つの方法は、実行可能なもの(オブジェクト、関数ポインターなど)を「to doリスト」のようなものに保存することです。例:
while (string code = GetKeyCodeFromSomewhere())
{
todo[code] = codefor[code];
}
各コード値に対して、適切な関数ポインター、または共通の基本クラスからサブクラス化されたオブジェクトを含むようにcodeforを初期化します。同じコードが複数回表示された場合、todoの適切なエントリは、既に持っていたのと同じ値で上書きされます。最後に、todoを反復処理し、すべてのメンバーを実行します。
確かにきれいではありませんが、特定の関数を呼び出したかどうかを示すフラグのセットを維持できます。こうすることで、セットに物を保存する必要がなくなり、フラグだけが得られます。
(おそらくそれが記述された方法から)あるので、異なるif / elseブロックのコンパイル時に固定されているので、ビットセットでこれを非常に簡単に行うことができます。
明らかに、特定の状況に依存しますが、呼び出す関数が既に実行されているかどうかを追跡し、必要に応じて早期に終了する方が良いかもしれません。