C++の map
はとても便利です。
高速なのもそうですが、存在しないキーを参照した時には 0
を返してくれるため、実装時のエラーハンドリングが不要になります。しかし、時としてこの仕様が仇になることがあります。
具体的には、0
を単位元として扱えないとき、よりわかりやすく言うと、最小値更新や最大値更新のときです。
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
int main() {
map<int, int> mp;
mp[1] = min(2, mp[1]);
cout << mp[1] << endl; // 0!?
}
最小値は 2
で更新されるはずですが 0
になっています。
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
int main() {
map<int, int> mp;
mp[1] = max(-2, mp[1]);
cout << mp[1] << endl; // 0!?
}
最大値は -2
で更新されるはずですが 0
になっています。
例えば、最小値だと初期値は十分大きな値、最大値だと初期値は十分小さな値、一般的には、ある演算に対して初期値を単位元にしないとバグります。
defaultfict
Python においては、map
に相当するデータ構造として defaultfdict
があります。(ビルトインの dict
だと、存在しないキーを参照した時にエラーが出るため、ハンドリングが必要になります)
defaultdict
はその名の通り初期値をカスタマイズできます。
from collections import defaultdict
# 最小値を求める場合(十分大きな初期値を設定)
min_map = defaultdict(lambda: 10**18)
min_map[1] = min(2, min_map[1])
print(min_map[1]) # 出力: 2
# 最大値を求める場合(十分小さな初期値を設定)
max_map = defaultdict(lambda: -10**18)
max_map[1] = max(-2, max_map[1])
print(max_map[1]) # 出力: -2
C++でもdefaultdictを
これと同じものを C++ でも定義しましょう。
以下のように、map
をオーバーライドして default_map
を作ります。
template <typename S, typename T>
struct default_map : map<S, T> {
T default_value;
// コンストラクタで初期値を受け取る
default_map(T default_value) : default_value(default_value) {}
// []演算子をオーバーライドして、キーがない場合にデフォルト値を設定
T& operator[](const S& key) {
return map<S, T>::emplace(key, default_value).first->second;
}
};
使用例
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
template <typename S, typename T>
struct default_map : map<S, T> {
T default_value;
// コンストラクタで初期値を受け取る
default_map(T default_value) : default_value(default_value) {}
// []演算子をオーバーライドして、キーがない場合にデフォルト値を設定
T& operator[](const S& key) {
return map<S, T>::emplace(key, default_value).first->second;
}
};
int main() {
default_map<int, int> min_map(2e9); // 十分大きな初期値(最小値用)
default_map<int, int> max_map(-2e9); // 十分小さな初期値(最大値用)
min_map[1] = min(2, min_map[1]);
cout << "min_map[1]: " << min_map[1] << endl; // 出力: 2
max_map[1] = max(-2, max_map[1]);
cout << "max_map[1]: " << max_map[1] << endl; // 出力: -2
return 0;
}
追記
コメントによると、あまり良くない実装らしいので、ご注意ください。一応、競プロのようなインスタントな用途を想定しています。