BGLといえばPropertyMap,PropertyMapといえばBGLなのだが,BGLのアダプタ関数はイテレータのペアであるRangeを返すなのでBGLとPropertyMap,Rangeをうまく組み合わせて使いたい.
で少し調べてみたら,BGL内にproperty_iter_rangeというのを発見.property_iteratorはイテレータが指し示す値に,その値をキーとするプロパティマップに適用するイテレータアダプタ.このペアを作るのがproperty_iter_range.
以下サンプル
#include <iostream> #include <random> #include <ctime> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/plod_generator.hpp> #include <boost/graph/property_iter_range.hpp> // property_iter_range #include <boost/graph/dijkstra_shortest_paths.hpp> #include <boost/property_map/property_map.hpp> #include <boost/range/adaptor/filtered.hpp> #include <boost/range/algorithm/generate.hpp> #include <boost/range/numeric.hpp> int main() { typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, boost::property<boost::vertex_root_t, bool, boost::property<boost::vertex_distance_t, int>>, boost::property<boost::edge_weight_t, int>> Graph; typedef boost::plod_iterator<std::mt19937, Graph> PlodGen; std::size_t const num_v = 20; std::mt19937 gen{ static_cast<std::mt19937::result_type>(time(nullptr)) }; // ランダムグラフの生成 Graph graph{ PlodGen{gen, num_v, 1.5, 200}, PlodGen{}, num_v }; std::uniform_int_distribution<> bool_dist{0, 1}; boost::generate( boost::get_property_iter_range(graph, boost::vertex_root), std::bind(bool_dist, gen)); std::uniform_int_distribution<> dist{0, 15}; boost::generate( boost::get_property_iter_range(graph, boost::edge_weight), std::bind(dist, gen)); boost::dijkstra_shortest_paths( graph, vertex(std::uniform_int_distribution<>{0, num_v - 1}(gen), graph), boost::weight_map(get(boost::edge_weight, graph)). distance_map(get(boost::vertex_distance, graph))); // rootから各vertexまでの距離の合計の計算 auto const is_root_func = boost::make_property_map_function(get(boost::vertex_root, graph)); auto const vrange = vertices(graph) | boost::adaptors::filtered(is_root_func); int total = 0; for (auto const v : vrange) { total += get(boost::vertex_distance, graph, v); } std::cout << total << std::endl; return 0; }
正直これはむむむ...propertyp_iteratorのレンジを得るにはget_property_iter_rangeという関数にグラフオブジェクトとPropertyTagのオブジェクトを渡す必要がある.property_iter_range.hppのファイルがPropertyMapではなくBGL内にあるからグラフに特化したインタフェースなのかもしれないが,これだと使いにくい.
例えば上のサンプルの最後の距離の合計の計算の場合,フィルタを掛けたvrangeにプロパティマップでマッピングできた方がいい.絶対にいい!
というわけでRangeで一般的なAdaptorを書いてみた.
https://github.com/amedama41/Canard/blob/master/property_map/property_map_adaptor.hpp
ついてでにBoostのproperty_iteratorはWritable PropertyMapには適用できなかったので,Writable PropertyMapにも対応させてみた.
これを使えばサンプルの計算も
boost::accumulate(
vrange | Canard::adaptors::mapped(get(boost::vertex_distance, graph)),
0.0);
みたいに書けていい感じなのではないだろうか