[Preface: The associative C++ containers like std::map are a bit like micro-databases with just one key column. Boost's bimap elevates this to a two-column table with lookup in both columns, but that that's as far as the analogy goes -- there's no "polymap" that generalizes the idea.]
In any event, I want to keep thinking of maps as databases, and I now wonder if there is an iterator (or some other solution) that allows me to do a UNION of several constituent maps. That is, all maps have the same type (or value type and comparator, at least), and I want a single iterator that treats the entire collection as a big multimap (repeated keys are OK) and lets me traverse it in the correct unioned order.
Does such a thing exist, perhaps within Boost? Or is it easy to rig one up? In pseudo code:
std::map<K, M> m1, m2;
union_iterator<K, M> u(m1, m2)
for(auto it = u.begin(); it != u.end(); ++it) { /* ... */ }
For example, if we had:
m1 = { { 9:00, "Check in"}, { 12:00, "Break" }, { 16:00, "Check out"} };
m2 = { { 10:30, "coffee" }, { 12:15, "baked beans" }, { 15:00, "lies" } };
then I want the iterator to produce:
9:00, "Check in"; 10:30, "coffee"; 12:00, "Break"; 12:15, "baked beans"; ...
There is a "polymap": Boost.MultiIndex.
As I announced, I have got something pretty cool.
I'm posting it now, because I wouldn't be sure whether I'd be back in time tonight to post it. I will be spending a few words in explanation. (in this post)
PS. The includes will be trimmed down (to about 20%); I will probably do some more general work on the code too.
A lot can be said about this code: it is not very efficient, and not very clean (yet). It is, however, nearly infinitely generic and should scale like anything else. All code can be found in a github gist:
merge_maps_iterator.hpp
Makefile
test.cpp - a rather arcane set of test-cases showing off the genericity (I'm not saying that it would be a good idea to have maps keyed with ints and floats (let alone both at the same time) - just showing that it can be done)
Here is the output of the test.cpp as you can find it:
== input ========================================
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
{ b, 3.14 }
== output =======================================
2: aap;
23: mies;
98: 3.14;
100: noot;
101: broer;
== input ========================================
{ b, 3.14 }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
2: aap;
23: mies;
98: 3.14;
100: noot;
101: broer;
== input ========================================
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
2: aap;aap;
23: mies;mies;
100: noot;noot;
101: broer;broer;
== input ========================================
{ b, 3.14 }
{ b, 3.14 }
== output =======================================
b: 3.14;3.14;
== input ========================================
{ 1.0, dag } { 22.0, bye } { 24.0, Tschüß }
{ 1, true } { 22, false } { 24, true }
{ b, 3.14 }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
1.0: dag;true;
2.0: aap;
22.0: bye;false;
23.0: mies;
24.0: Tschüß;true;
98.0: 3.14;
100.0: noot;
101.0: broer;
== input ========================================
{ 1.0, dag } { 2.0, EXTRA } { 22.0, bye } { 24.0, Tschüß }
{ 1, true } { 22, false } { 24, true }
{ b, 3.14 }
{ 2, aap } { 23, mies } { 100, noot } { 101, broer }
== output =======================================
1.0: dag;true;
2.0: EXTRA;aap;
22.0: bye;false;
23.0: mies;
24.0: Tschüß;true;
98.0: 3.14;
100.0: noot;
101.0: broer;
Either copying both mapS into a temporary, appending one to the other (in case you can modify them) or using a vector as a temporary with std::set_union and a custom comparator are the easiest alternative solutions.
Here's how I would implement thiton's answer:
template <class container> class union_iterator
{
private:
typedef std::pair<typename container::const_iterator, typename container::const_iterator> container_range;
class container_range_compare
{
public:
bool operator()(const container_range &lhs, const container_range &rhs) const
{
return typename container::value_compare()(*lhs.first, *rhs.first);
}
};
std::priority_queue<container_range, container_range_compare> m_range_queue;
container::const_iterator m_current_iterator;
bool m_is_valid;
void add_container(const container &cont)
{
add_container_range(std::make_pair(cont.begin(), cont.end()));
}
void add_container_range(const container_range &range)
{
if (range.first!=range.second)
{
m_range_queue.push(range);
}
}
public:
union_iterator(const container &a): m_valid(false)
{
add_container(a);
}
bool next()
{
m_is_valid= false;
if (!m_range_queue.empty())
{
container_range range= m_range_queue.pop();
m_current_iterator= range.first;
++range.first;
add_container_range(range);
m_is_valid= true;
}
return m_is_valid;
}
typename const container::value_type &operator *() const
{
return *m_current_iterator;
}
typename const container::value_type *operator ->() const
{
return m_current_iterator.operator ->();
}
};
It has slightly different usage than union_iterator<K, V> but it implements the basic idea. You can expand the constructor to accept multiple maps however you fit, and use it in a while (iterator.next()) loop instead of a for (...) loop.
EDIT: I simplified next() by doing all the popping and pushing at once. So now it's even simpler! (One could also expend some effort making it like a STL iterator, but that gets tedious.)
Very simple solution using boost function_output_iterator:
typedef std::map< std::string, std::string > Map;
Map first_map, second_map;
... // fill maps
// iterate over maps union
std::merge(
first_map.begin(), first_map.end(),
second_map.begin(), second_map.end(),
boost::make_function_output_iterator(
[]( const Map::value_type & pair )
{
std::cout <<
"key = " << pair.first <<
"; value = " << pair.second << std::endl;
}
),
first_map.value_comp()
);
We can make this solution prettier by using boost::set_union (range version) instead of std::set_union.
UPD Updated version use different key/values types:
typedef std::map< int, char > FirstMap;
typedef std::map< short, std::string > SecondMap;
FirstMap first_map;
SecondMap second_map;
... // fill maps
struct CustomOutput
{
void operator()( const FirstMap::value_type & pair ) const
{
std::cout << "key = " << pair.first <<
"; value = " << pair.second << std::endl;
}
void operator()( const SecondMap::value_type & pair ) const
{
std::cout << "key = " << pair.first <<
"; value = " << pair.second << std::endl;
}
};
struct CustomPred
{
bool operator()( const FirstMap::value_type & first_pair, const SecondMap::value_type & second_pair ) const
{ return first_pair.first < second_pair.first; }
bool operator()( const SecondMap::value_type & second_pair, const FirstMap::value_type & first_pair ) const
{ return second_pair.first < first_pair.first; }
};
// iterate over maps union
std::merge(
first_map.begin(), first_map.end(),
second_map.begin(), second_map.end(),
boost::make_function_output_iterator( CustomOutput() ),
CustomPred()
);
UPD2 std::set_union replaced with std::merge
Or is it easy to rig one up?
Rigging up should be fairly easy: For N base maps, your iterator contains a priority queue prioritized by the N keys of the elements the base iterators point to. For dereference, dereference the iterator at the queue front. For increment, increment the iterator at the queue front and, if it's increment is not at the end, re-insert it.
Here's how it can be done quite easily:
template<class It>
class union_iterator
{
public:
union_iterator(It it1_begin, It it1_end, It it2_begin, It it2_end)
: current1(it1_begin), current2(it2_begin), end1(it1_end), end2(it2_end)
{ if (it1_begin != it1_end && it2_begin != it2_end) {
if (*it1_begin < *it2_begin) { current= ¤t1; }
else { current = ¤t2; }
} else if (it1_begin==it1_end) { current=¤t2; }
else { current = ¤t1; }
}
void operator++() {
if (current1!=end1 && current2 !=end2) {
if (*current1 < *current2)
{ ++current1; current = ¤t1; }
else { ++current2; current=¤t2; }
} else if (current1==end1 && current2 != end2) {
++current2;
current = ¤t2;
} else if (current1!=end1 && current2 == end2) {
++current1;
current = ¤t1;
}
}
typename std::iterator<It1>::value_type operator*() { return **current; }
private:
It current1;
It current2;
It end1;
It end2;
It *current;
};
But the real problem is implementing all the remaining member functions required by normal iterators :-). Boost has some lib for helping you do it, but it might still be quite difficult.
This isn't an iterator like you asked for, but I just found this function in the standard library:
§ 25.4.5.2 set_union [set.union]
template<class InputIterator1, class InputIterator2,
class OutputIterator, class Compare>
OutputIterator
set_union(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result, Compare comp);
Effects: Constructs a sorted intersection of the elements from the two ranges; that is, the set of elements that are present in both of the ranges.
Requires: The resulting range shall not overlap with either of the original ranges.
Returns: The end of the constructed range.
Complexity: At most 2 * ((last1 - first1) + (last2 - first2)) - 1 comparisons.
Remarks: If [first1,last1) contains m elements that are equivalent to each other and [first2, last2) contains n elements that are equivalent to them, the first min(m, n) elements shall be copied from the first range to the output range, in order.
There's also a std::set_intersection, std::set_difference, and std::set_symmetric_difference
Related
I have algorithm that uses iterators, but there is a problem with transforming values, when we need more than single source value.
All transform iterators just get some one arg and transforms it. (see similar question from the past)
Code example:
template<typename ForwardIt>
double some_algorithm(ForwardIt begin, ForwardIt end) {
double result = 0;
for (auto it = begin; it != end; ++it) {
double t = *it;
/*
do some calculations..
*/
result += t;
}
return result;
}
int main() {
{
std::vector<double> distances{ 1, 2, 3, 4 };
double t = some_algorithm(distances.begin(), distances.end());
std::cout << t << std::endl;
/* works great */
}
{
/* lets now work with vector of points.. */
std::vector<double> points{ 1, 2, 4, 7, 11 };
/* convert to distances.. */
std::vector<double> distances;
distances.resize(points.size() - 1);
for (size_t i = 0; i + 1 < points.size(); ++i)
distances[i] = points[i + 1] - points[i];
/* invoke algorithm */
double t = some_algorithm(distances.begin(), distances.end());
std::cout << t << std::endl;
}
}
Is there a way (especialy using std) to create such an iterator wrapper to avoid explicitly generating distances value?
It could be fine to perform something like this:
template<typename BaseIterator, typename TransformOperator>
struct GenericTransformIterator {
GenericTransformIterator(BaseIterator it, TransformOperator op) : it(it), op(op) {}
auto operator*() {
return op(it);
}
GenericTransformIterator& operator++() {
++it;
return *this;
}
BaseIterator it;
TransformOperator op;
friend bool operator!=(GenericTransformIterator a, GenericTransformIterator b) {
return a.it != b.it;
}
};
and use like:
{
/* lets now work with vector of points.. */
std::vector<double> points{ 1, 2, 4, 7, 11 };
/* use generic transform iterator.. */
/* invoke algorithm */
auto distance_op = [](auto it) {
auto next_it = it;
++next_it;
return *next_it - *it;
};
double t = some_algorithm(
generic_transform_iterator(points.begin(), distance_op),
generic_transform_iterator(points.end() -1 , distance_op));
std::cout << t << std::endl;
}
So general idea is that transform function is not invoked on underlying object, but on iterator (or at least has some index value, then lambda can capture whole container and access via index).
I used to use boost which has lot of various iterator wrapping class.
But since cpp20 and ranges I'm curious if there is a way to use something existing from std:: rather than writing own wrappers.
With C++23, use std::views::pairwise.
In the meantime, you can use iota_view. Here's a solution which will work with any bidirectional iterators (e.g. points could be a std::list):
auto distances =
std::views::iota(points.cbegin(), std::prev(points.cend()))
| std::views::transform([](auto const &it) { return *std::next(it) - *it; });
This can also be made to work with any forward iterators. Example:
std::forward_list<double> points{1, 2, 4, 7, 11};
auto distances =
std::views::iota(points.cbegin())
| std::views::take_while([end = points.cend()](auto const &it) { return std::next(it) != end; })
| std::views::transform([](auto const &it) { return *std::next(it) - *it; })
| std::views::common;
Note that both of these snippets have undefined behaviour if points is empty.
I'm not sure this addresses your problem (let me know if it doesn't and I'll remove the answer), but you may be able to achieve that with ranges (unfortunately, not with standard ranges yet, but Eric Niebler's range-v3).
The code below:
groups the points vector in pairs,
calculates the difference between the second and the first element of each pair, and then
sums all those differences up.
[Demo]
auto t{ accumulate(
points | views::sliding(2) | views::transform([](const auto& v) { return v[1] - v[0]; }),
0.0
)};
I find myself often with code that looks like this:
bool isFirst = true;
for(const auto &item: items)
{
if(!isFirst)
{
// Do something
}
// Normal processing
isFirst = false;
}
It seems that there ought to be a better way to express this, since it's a common pattern in functions that act like a "join".
Maybe a for_first_then_each is what you're looking for? It takes your range in terms of iterators and applies the first function to the first element and the second function to the rest.
#include <iostream>
#include <vector>
template<typename BeginIt, typename EndIt, typename FirstFun, typename OthersFun>
void for_first_then_each(BeginIt begin, EndIt end, FirstFun firstFun, OthersFun othersFun) {
if(begin == end) return;
firstFun(*begin);
for(auto it = std::next(begin); it != end; ++it) {
othersFun(*it);
};
}
int main() {
std::vector<int> v = {0, 1, 2, 3};
for_first_then_each(v.begin(), v.end(),
[](auto first) { std::cout << first + 42 << '\n'; },
[](auto other) { std::cout << other - 42 << '\n'; }
);
// Outputs 42, -41, -40, -39
return 0;
}
You can't know which element you are visiting in a range based for loop unless you are looping over a container like an array or vector where you can take the address of the object and compare it to the address of the first item to figure out where in the container you are. You can also do this if the container provides lookup by value, you can see if the iterator returned from the find operation is the same as the begin iterator.
If you need special handling for the first element then you can fall back to a traditional for loop like
for (auto it = std::begin(items), first = it, end = std::end(items); it != end; ++it)
{
if (it == first)
{
// do something
}
// Normal processing
}
If what you need to do can be factored out of the loop then you could use a range based for loop and just put the processing before the loop like
// do something
for(const auto &item: items)
{
// Normal processing
}
With Ranges coming in C++20, you can split this in two loops:
for (auto const& item : items | view::take(1)) {
// first element only (or never executed if items is empty)
}
for (auto const& item : items | view::drop(1)) {
// all after the first (or never executed if items has 1 item or fewer)
}
If you don't want to wait for C++20, check out range-v3 which supports both of these operations.
This won't work like this with an Input range (like if items is really a range that reads from cin) but will work just fine with any range that is Forward or better (I'm guessing items is a container here, so that should be fine).
A more straightforward version is actually to use enumerate (which only exists in range-v3, not in C++20):
for (auto const& [idx, item] : view::enumerate(items)) {
if (idx == 0) {
// first element only
}
// all elements
}
A fun alternative solution, that I would not use in production without great care, would be to use custom iterator.
int main() {
std::vector<int> v{1,2,3,4};
for (const auto & [is_first,b] : wrap(v)) {
if (is_first) {
std::cout << "First: ";
}
std::cout << b << std::endl;
}
}
A toy implementation could look like this:
template<typename T>
struct collection_wrap {
collection_wrap(T &c): c_(c) {}
struct magic_iterator {
bool is_first = false;
typename T::iterator itr;
auto operator*() {
return std::make_tuple(is_first, *itr);
}
magic_iterator operator++() {
magic_iterator self = *this;
itr++;
//only works for forward
is_first = false;
return self;
}
bool operator!=(const magic_iterator &o) {
return itr != o.itr;
}
};
magic_iterator begin() {
magic_iterator itr;
itr.is_first = true;
itr.itr = c_.begin();
return itr;
}
magic_iterator end() {
magic_iterator itr;
itr.is_first = false;
itr.itr = c_.end();
return itr;
}
T &c_;
};
template<typename Collection>
collection_wrap<Collection>
wrap(Collection &vec) {
return collection_wrap(vec);
}
Check the object address to see if it's the first item:
for(const auto &item: items)
{
if (&item != &(*items.begin())
{
// do something for all but the first
}
// Normal processing
}
An approach still valid in C++ is to use a macro:
#include <iostream>
#include <vector>
#define FOR(index, element, collection, body) { \
auto &&col = collection; \
typeof(col.size()) index = 0; \
for(auto it=col.begin(); it!=col.end(); index++, it++) { \
const auto &element = *it; \
body; \
} \
}
using namespace std;
int main() {
vector<int> a{0, 1, 2, 3};
FOR(i, e, a, {
if(i) cout << ", ";
cout << e;
})
cout << endl;
FOR(i, e, vector<int>({0, 1, 2, 3}), {
if(i) cout << ", ";
cout << e;
})
cout << endl;
return 0;
}
Prints:
0, 1, 2, 3
0, 1, 2, 3
This solution is succinct compared to alternative options. On the downside, index is being tested and incremented on each iteration of the loop - this can be avoided by increasing the complexity of the macro and by using bool first instead of index, but using index in the macro covers more use cases than bool first.
Since C++20, you can slightly improve your range-based for loop by using an init-statement. The init-statement allows you to move your isFirst flag into the scope of the loop so that this flag is no longer visible outside the loop:
std::vector<int> items { 1, 2, 3 };
for(bool isFirst(true); const auto &item: items) {
if(!isFirst) {
std::cout << "Do something with: " << item << std::endl;
}
std::cout << "Normal processing: " << item << std::endl;
isFirst = false;
}
Output:
Normal processing: 1
Do something with: 2
Normal processing: 2
Do something with: 3
Normal processing: 3
Code on Wandbox
I assume you want to know how to retrieve the first element, you could do this with an array and a vector.
I'm going to show the array here.
First include this in your code:
#include <array>
Then convert your array accordingly:
std::array<std::string, 4> items={"test1", "test2", "test3", "test4"};
for(const auto &item: items)
{
if(item == items.front()){
// do something
printf("\nFirst: %s\n", item.c_str()); //or simply printf("\nFirst:"); since you gonna output a double element
}
// Normal processing
printf("Item: %s\n", item.c_str());
}
return 0;
}
Ok, so this is an interview question that I got, and only performed mediocre on at the time. I am wondering what the optimal solution is and how it is best implemented.
You are given multiple sorted lists, construct something that allows us to iterate over all these lists from the smallest to the largest element.
Example:
{ -2, 5, 10}
{ 2, 9, 11}
{ -5, 9}
-> -5, -2, 2, 5, 9, 9, 10, 11
Update:
With a bit of help from the SO chat #c-questions-and-answers and #Nican in particular, I've gotten this ship to fly somehow. I have posted my working code as an answer to allow for other solutions as well.
The answer I have posted below is still messy, and in particular I have not implemented == and != correctly. I still need help on those.
Justification for this question
Finding clean and minimalistic custom iterator implementations online is not that common. And I believe this question may serve as a good starting point for others to enhance their understanding of iterators and best practices.
I think that SortedListsIter::iterator should contain a copy of all the items, rather than reference, so that you can be ForwardIterator instead of InputIterator. You also avoid the dangling reference in the end iterator (which can be a default constructed iterator, as iterator::iterator(std::vector<SortedListIterItem<Iter> > _items = {}) : m_items(_items){};)
Two heaps may differ in order of elements, so we use std::is_permutation to determine equality
bool SortedListsIter::iterator::operator==(iterator other)
{ return std::is_permutation(m_items.begin(), m_items.end(), other.m_items.begin(), other.m_items.end()); }
C++11 alternative (4 iterator version that checks distance isn't available):
bool SortedListsIter::iterator::operator==(iterator other)
{ return (std::distance(m_items.begin(), m_items.end()) == std::distance(other.m_items.begin(), other.m_items.end()))
&& std::is_permutation(m_items.begin(), m_items.end(), other.m_items.begin()); }
Item equality is simple:
bool SortedListIterItem::operator==(SortedListIterItem other)
{ return (it_beg == other.it_beg) && (it_end == other.it_end); }
This is not a complete answer
I've implemented a partly solution to the point that it works. This is not a complete, nor correct implementation in lines with the requirements of a input_iterator. This illustrates the point, and the remaining legwork is up to whoever feels the call.
--
I just picked this up again from my notes and efforts yesterday. I've gotten some really nice help from Nican.
I'm using a heap to keep the lists inside my structure. (Which has the valid critique that I am reinventing the priority_queue). There are several things still missing here, among other things:
Copy constructor
Post-fix ++ operator
Proper == and != implementation, I'm only comparing size.
This could easily be a forward_iterator.
I got to the point where I've built my understanding of iterators. And this is as far as I'm going to take it this time around.
#include <algorithm>
#include <forward_list>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
template <typename Iter>
struct SortedListIterItem {
Iter it_beg;
Iter it_end;
/* Used by the heap to sort ascending */
bool operator<(const SortedListIterItem<Iter>& other) {
return *it_beg > *other.it_beg;
}
bool operator==(const SortedListIterItem<Iter>& other) {
return it_beg == other.it_begin && it_end == *other.it_beg;
}
SortedListIterItem<Iter>(Iter _begin, Iter _end)
: it_beg(_begin), it_end(_end){};
};
template <typename Iter>
class SortedListsIter {
// member typedefs provided through inheriting from std::iterator
class iterator {
std::vector<SortedListIterItem<Iter> > m_items;
public:
iterator(std::vector<SortedListIterItem<Iter> > _items = {})
: m_items(_items){};
iterator& operator++() {
std::pop_heap(m_items.begin(), m_items.end());
SortedListIterItem<Iter>& largest = m_items.back();
if (++largest.it_beg == largest.it_end) {
m_items.pop_back();
} else {
std::push_heap(m_items.begin(), m_items.end());
}
return *this;
}
// iterator traits
using value_type = typename Iter::value_type;
using pointer = typename Iter::pointer;
using reference = typename Iter::reference;
using iterator_category = std::input_iterator_tag;
/** A simplified comparator, which is not a correct implementation.
* While it does work for regular for loops. */
bool operator!=(iterator other) {
return (m_items.size() != other.m_items.size());
}
value_type operator*() const {
return *(m_items.front().it_beg);
};
};
std::vector<SortedListIterItem<Iter> > m_items;
public:
void add_list(Iter it_begin, Iter it_end) {
if (it_begin != it_end) {
m_items.push_back(SortedListIterItem<Iter>(it_begin, it_end));
std::push_heap(m_items.begin(), m_items.end());
}
// Ignore empty lists.
}
iterator begin() { return iterator(m_items); };
iterator end() {
std::vector<SortedListIterItem<Iter> > _items;
return iterator(_items);
};
};
int main(int argc, char** argv) {
std::forward_list<std::string> animals = {"Cat", "Dog", "Horse"};
std::forward_list<std::string> fish = {"Dolphin", "Mermaid", "Shark"};
std::forward_list<std::string> birds = {"Crow", "Duck", "Eagle"};
SortedListsIter<std::forward_list<std::string>::iterator> my_string_iter;
my_string_iter.add_list(fish.begin(), fish.end());
my_string_iter.add_list(animals.begin(), animals.end());
my_string_iter.add_list(birds.begin(), birds.end());
for (auto i : my_string_iter) {
std::cout << " " << i << ",";
}
std::cout << std::endl;
for (auto i : my_string_iter) {
std::cout << " " << i << ",";
}
std::cout << std::endl;
std::vector<int> l4 = {1, 2, 99};
std::vector<int> l5 = {-11, -4, 3};
std::vector<int> l6 = {-5, 1};
SortedListsIter<std::vector<int>::iterator> my_iter2;
my_iter2.add_list(l4.begin(), l4.end());
my_iter2.add_list(l5.begin(), l5.end());
my_iter2.add_list(l6.begin(), l6.end());
for (auto i : my_iter2) {
std::cout << " " << i << ",";
}
std::cout << std::endl;
return 0;
}
I need to implement the following datastructure for my project. I have a relation of
const MyClass*
to
uint64_t
For every pointer I want to save a counter connected to it, which can be changed over time (in fact only incremented). This would be no problem, I could simply store it in a std::map. The problem is that I need fast access to the pointers which have the highest values.
That is why I came to the conclusion to use a boost::bimap. It is defined is follows for my project:
typedef boost::bimaps::bimap<
boost::bimaps::unordered_set_of< const MyClass* >,
boost::bimaps::multiset_of< uint64_t, std::greater<uint64_t> >
> MyBimap;
MyBimap bimap;
This would work fine, but am I right that I can not modify the uint64_t on pair which were inserted once? The documentation says that multiset_of is constant and therefore I cannot change a value of pair in the bimap.
What can I do? What would be the correct way to change the value of one key in this bimap? Or is there a simpler data structure possible for this problem?
Here's a simple hand-made solution.
Internally it keeps a map to store the counts indexed by object pointer, and a further multi-set of iterators, ordered by descending count of their pointees.
Whenever you modify a count, you must re-index. I have done this piecemeal, but you could do it as a batch update, depending on requirements.
Note that in c++17 there is a proposed splice operation for sets and maps, which would make the re-indexing extremely fast.
#include <map>
#include <set>
#include <vector>
struct MyClass { };
struct store
{
std::uint64_t add_value(MyClass* p, std::uint64_t count = 0)
{
add_index(_map.emplace(p, count).first);
return count;
}
std::uint64_t increment(MyClass* p)
{
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return add_value(p, 1);
}
else {
remove_index(it);
++it->second;
add_index(it);
return it->second;
}
}
std::uint64_t query(MyClass* p) const {
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return 0;
}
else {
return it->second;
}
}
std::vector<std::pair<MyClass*, std::uint64_t>> top_n(std::size_t n)
{
std::vector<std::pair<MyClass*, std::uint64_t>> result;
result.reserve(n);
for (auto idx = _value_index.begin(), idx_end = _value_index.end() ;
n && idx != idx_end ;
++idx, --n) {
result.emplace_back((*idx)->first, (*idx)->second);
}
return result;
}
private:
using map_type = std::map<MyClass*, std::uint64_t>;
struct by_count
{
bool operator()(map_type::const_iterator l, map_type::const_iterator r) const {
// note: greater than orders by descending count
return l->second > r->second;
}
};
using value_index_type = std::multiset<map_type::iterator, by_count>;
void add_index(map_type::iterator iter)
{
_value_index.emplace(iter->second, iter);
}
void remove_index(map_type::iterator iter)
{
for(auto range = _value_index.equal_range(iter);
range.first != range.second;
++range.first)
{
if (*range.first == iter) {
_value_index.erase(range.first);
return;
}
}
}
map_type _map;
value_index_type _value_index;
};
I'm teaching my self C++.
I'm trying to combine polynomials. For this I have defined straightforward classes:
Polynomial<T>, Term<T> and Coefficient<T> (which may also just be
complex<T>) using simple value composition. I have defined the required operator overloads.
Polynomial's compare by sorting their terms (std::sort).
I am working on combineLikeTerms(); This method when called will first call
another member method that will sort this vector of Terms. For example:
4x^3 + 5x^2 + 3x - 4
would be a possible resulting sorted vector.
Question:
I am using two iterators on this vector and Im trying to merge adjacent terms
of the same order.
Lets say our initial vector after being sorted is this:
4x^3 - 2x^3 + x^3 - 2x^2 + x ...
after the function completes its iterations the temp stack vector would then
look like this 2x^3 + x^3 - 2x^2 + x ... if we look there are still like terms
this needs to be refactored again.
How do I do this? I'm thinking of using recursion.
// ------------------------------------------------------------------------- //
// setPolynomialByDegreeOfExponent()
// should be called before combineLikeTerms
template <class T>
void Polynomial<T>::setPolynomialByDegreeOfExponent()
{
unsigned int uiIndex = _uiNumTerms - 1;
if ( uiIndex < 1 )
{
return;
}
struct _CompareOperator_
{
bool operator() ( math::Term<T> a, Term<T> b )
{
return ( a.getDegreeOfTerm() > b.getDegreeOfTerm() );
} // operator()
};
stable_sort( _vTerms.begin(), _vTerms.end(), _CompareOperator_() );
} // setPolynomialByDegreeOfExponent
// ------------------------------------------------------------------------- //
// addLikeTerms()
template <class T>
bool Polynomial<T>::addLikeTerms( const Term<T>& termA, const Term<T>& termB, Term<T>& result ) const
{
if ( termA.termsAreAlike( termB ) )
{
result = termA + termB;
return true;
}
return false;
} // addLikeTerms
// ------------------------------------------------------------------------- //
// combineLikeTerms()
template <class T>
void Polynomial<T>::combineLikeTerms()
{
// First We Order Our Terms.
setPolynomialByDegreeOfExponent();
// Nothing To Do Then
if ( _vTerms.size() == 1 )
{
return;
}
Term<T> result; // Temp Variable
// No Need To Do The Work Below This If Statement This Is Simpler
if ( _vTerms.size() == 2 )
{
if ( addLikeTerms( _vTerms.at(0), _vTerms.at(1) )
{
_vTerms.clear();
_vTerms.push_back( result );
}
return;
}
// For 3 Ore More Terms
std::vector<Term<T>> vTempTerms; // Temp storage
std::vector<Term<T>>::iterator it = _vTerms.begin();
std::vector<Term<T>>::iterator it2 = _vTerms.begin()+1;
bool bFound = addLikeTerms( *it, *it2, result );
while ( it2 != _vTerms.end() )
{
if ( bFound )
{
// Odd Case Last Three Elems
if ( (it2 == (_vTerms.end()-2)) && (it2+1) == (_vTerms.end()-1)) )
{
vTempTerms.push_back( result );
vTempTerms.push_back( _vTerms.back() );
break;
}
// Even Case Last Two Elems
else if ( (it2 == (_vTerms.end()-1)) && (it == (_vTerms.end()-2)) )
{
vTempTerms.push_back( result );
break;
}
else
{
vTempTerms.push_back( result );
it += 2; // Increment by 2
it2 += 2; "
bFound = addLikeTerms( *it, *it2, result );
}
}
else {
// Push Only First One
vTempTerms.push_back( *it );
it++; // Increment By 1
it2++; "
// Test Our Second Iterator
if ( it2 == _vTerms.end() )
{
vTempTerms.push_back( *(--it2) ); // same as using _vTerms.back()
}
else
{
bFound = addLikeTerms( *it, *it2, result );
}
}
}
// Now That We Have Went Through Our Container, We Need To Update It
_vTerms.clear();
_vTerms = vTempTerms;
// At This point our stack variable should contain all elements from above,
// however this temp variable can still have like terms in it.
// ??? Were do I call the recursion and how do I define the base case
// to stop the execution of the recursion where the base case is a
// sorted std::vector of Term<T> objects that no two terms that are alike...
// I do know that the recursion has to happen after the above while loop
} // combineLikeTerms
Can someone help me find the next step? I'd be happy to hear about any bugs/efficiency issues in the code shown.
I love c++
Here's my take on it in modern C++.
Note the extra optimization of dropping terms with an effective coefficient of zero
Self contained sample: http://liveworkspace.org/code/ee68769826a80d4c7dc314e9b792052b
Update: posted a c++03 version of this http://ideone.com/aHuB8
#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
template <typename T>
struct Term
{
T coeff;
int exponent;
};
template <typename T>
struct Poly
{
typedef Term<T> term_t;
std::vector<term_t> _terms;
Poly(std::vector<term_t> terms) : _terms(terms) { }
void combineLikeTerms()
{
if (_terms.empty())
return;
std::vector<term_t> result;
std::sort(_terms.begin(), _terms.end(),
[] (term_t const& a, term_t const& b) { return a.exponent > b.exponent; });
term_t accum = { T(), 0 };
for(auto curr=_terms.begin(); curr!=_terms.end(); ++curr)
{
if (curr->exponent == accum.exponent)
accum.coeff += curr->coeff;
else
{
if (accum.coeff != 0)
result.push_back(accum);
accum = *curr;
}
}
if (accum.coeff != 0)
result.push_back(accum);
std::swap(_terms, result); // only update if no exception
}
};
int main()
{
Poly<int> demo({ { 4, 1 }, { 6, 7 }, {-3, 1 }, { 5, 5 } });
demo.combineLikeTerms();
for (auto it = demo._terms.begin(); it!= demo._terms.end(); ++it)
std::cout << (it->coeff>0? " +" : " ") << it->coeff << "x^" << it->exponent;
std::cout << "\n";
}
You need to look at the polynomial as a sequence of pairs (coefficient,variable):
[(coefficient1,variable1),(coefficient2,variable2),(coefficient3,variable3),...]
As you describe, you iterate through this from left to right, combining two adjacent pairs into one whenever the variable part is identical (this of course assumes that the list has already been sorted by the variable part!).
Now what happens when there are three or more elements in this list that share their variables? Well, then just keep combining them. There is no need for recursion or anything complicated, really.
At any point during the iteration you combine the variable part of the current pair with the variable part last seen. If they are identical, you combine them and simply continue. If the next pair you get still has the same variable part as the one last seen, well then you combine them again. If you do this correctly, there shouldn't be any duplicates left.
Here is an example of how to do this. It works by creating a new pair list, then iterating through the input list, for each item of the input list it decides whether to either combine it with the item last pushed to the new list, or by adding a new element to the new list:
#include <utility>
#include <vector>
#include <iostream>
typedef std::vector<std::pair<float,std::string>> Polynomial;
Polynomial combine_like_terms(const Polynomial &poly)
{
if (poly.empty())
return poly;
/* Here we store the new, cleaned-up polynomial: */
Polynomial clean_poly;
/* Now we iterate: */
auto it = begin(poly);
clean_poly.push_back(*it);
++it;
while (it != end(poly)) {
if (clean_poly.back().second == it->second)
clean_poly.back().first += it->first; // Like term found!
else
clean_poly.push_back(*it); // Sequence of like-terms ended!
++it;
}
return clean_poly;
}
int main()
{
Polynomial polynomial {
{ 1.0 , "x^2" },
{ 1.4 , "x^3" },
{ 2.6 , "x^3" },
{ 0.2 , "x^3" },
{ 2.3 , "x" },
{ 0.7 , "x" }
};
Polynomial clean_polynomial = combine_like_terms(polynomial);
for (auto term : clean_polynomial)
std::cout << '(' << term.first << ',' << term.second << ")\n";
std::cout.flush();
return 0;
}
You can easily make this templated again if you need to – I used float for the coefficients and strings for the variable part. It's really just a code example to show how this can be done easily without recursion or lots of iterators used in parallel.
Oh, and the code is written for C++11. Again, it's just a model and can be adjusted for C++03.