Advice on cleaning up this termination condition - c++

I've been playing with C++11 functional in order to do the same as python's itertools.combinations(input, 2), so far this is what I have:
EDIT removed outer lambda as suggested by #DavidRodríguez-dribeas
#include <iostream>
#include <functional>
#include <vector>
using namespace std;
template <class T>
function<pair<T*, T*>()> combinations(vector<T> & input) {
auto it1 = input.begin();
auto end = input.end();
auto it2 = next(it1);
return [=]() mutable {
if (it2 == end) {
it1++;
it2 = next(it1);
}
if (it2 != end)
return pair<T*,T*>(&(*it1), &(*it2++));
return pair<T*,T*>(&*end, &*end);
};
};
int main (void) {
vector<int> numbers{1,2,3,4,5,6};
auto func = combinations(numbers);
while ( true ) {
auto i = func();
if (i.first == &*(numbers.end())) break;
cout << *(i.first) << ',' << *(i.second) << endl;
}
return 0;
};
I'm not happy with the method used to iterate over the combinations any advice on cleaning it up?

Here is documentation and code on my favorite way of doing this. And here is how that library would be used for your example:
#include <iostream>
#include <vector>
#include "combinations"
using namespace std;
int main (void) {
vector<int> numbers{1,2,3,4,5,6};
for_each_combination(numbers.begin(), numbers.begin()+2, numbers.end(),
[](vector<int>::const_iterator b, vector<int>::const_iterator e)
{
if (b != e)
{
cout << *b;
for (auto i = b+1; i != e; ++i)
cout << ',' << *i;
cout << endl;
}
return false;
});
}
1,2
1,3
1,4
1,5
1,6
2,3
2,4
2,5
2,6
3,4
3,5
3,6
4,5
4,6
5,6
Should the need arise, it is trivial to change the example use to consider 3 or 4 items at time instead of 2. One can also deal with various permutations k out of N at a time.
Update
Adding a level of indirection to illustrate how you would deal with a vector of items that were not efficient at moving/swapping around in the vector:
#include <iostream>
#include <vector>
#include "combinations"
using namespace std;
int main (void) {
vector<int> numbers{1,2,3,4,5,6};
vector<vector<int>::const_iterator> num_iters;
num_iters.reserve(numbers.size());
for (auto i = numbers.begin(); i != numbers.end(); ++i)
num_iters.push_back(i);
for_each_combination(num_iters.begin(), num_iters.begin()+2, num_iters.end(),
[](vector<vector<int>::const_iterator>::const_iterator b,
vector<vector<int>::const_iterator>::const_iterator e)
{
if (b != e)
{
cout << **b;
for (auto i = b+1; i != e; ++i)
cout << ',' << **i;
cout << endl;
}
return false;
});
}

I found out that Oliver Kowalke's coroutine library has been accepted by Boosts peer review and should be included hopefully in the next version. Jumping the gun a bit I gave it a go by using the coroutine branch of the boost-dev repo (https://gitorious.org/boost-dev/boost-dev).
g++ -I path/to/boost-dev -std=c++11 test_code.cpp -o run_test_code -static -L path/to/boost-dev/stage/lib/ -lboost_context
#include <boost/coroutine/all.hpp>
#include <boost/bind.hpp>
#include <boost/range.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace boost;
template <typename T>
using coro_pairT_void = coroutines::coroutine<pair<T&,T&>(void)>;
template <typename T>
void combinations(typename coro_pairT_void<T>::caller_type & self, vector<T> & input ) {
for (auto it1 = input.begin(), itend = input.end(); it1 != itend; it1++) {
for (auto it2 = std::next(it1); it2 != itend; it2++) {
self(pair<T&, T&>(*it1,*it2));
}
}
};
int main( void ) {
vector<int> numbers{1,2,3,4,5,6};
coro_pairT_void<int> func(bind(combinations<int>, _1, numbers));
for (auto it(begin(func)), itend(end(func)); it != itend; ++it) {
cout << it->first << ',' << it->second << endl;
}
return 0;
};

Related

Is there a way to convert local_iterator to const_iterator or iterator?

How can we change unordered_multimap::local_iterator to unordered_multimap::iterator or unordered_multimap::const_iterator?
I need the change because we cannot erase elements using local_iterator, and erasing can only be done using iterator/const_iterator. If there is any other way of erasing using local_iterator, please do suggest.
Kinda labor intensive, but you can iterate through the result of equal_range() until you find the correct iterator:
template<typename Cont>
typename Cont::iterator local_to_regular_iterator(Cont& c, typename Cont::local_iterator local_ite) {
auto range = c.equal_range(local_ite->first);
for(auto ite = range.first; ite != range.second; ++ite) {
if(&ite->second == &local_ite->second) {
return ite;
}
}
throw std::out_of_range("huh?");
}
As far as I can tell, that's as good as you can get currently.
You could use find_if algorithm to search for an element with the same address like so:
auto it = std::ranges::find_if(m,
[ptr = std::addressof(*local_it)](const auto& e) -> bool
{
return ptr == std::addressof(e);
}
);
Here is a full code snippet:
#include <string_view>
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <ranges>
#include <memory>
int main()
{
std::unordered_multimap<int, char> m;
m.insert({1, 'a'});
m.insert({1, 'b'});
m.insert({2, 'c'});
auto local_it = m.begin(m.bucket(1));
std::cout << local_it->first << local_it->second << '\n';
auto it = std::ranges::find_if(m,
[ptr = std::addressof(*local_it)](const auto& e) -> bool
{
return ptr == std::addressof(e);
}
);
std::cout << it->first << it->second << '\n';
return 0;
}
Run it here.

Solution to c2100 with iterator?

So here is my program with the error:
// ConsoleApplication42.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <list>
#include <cstdlib>
#include <time.h>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
const int numberOfStudents = 9;
string names[numberOfStudents] = {"Abe","Billy","Carl","Dillan","Eddie","Felix","Gill","Herald","Isaac"};
struct StudentInfo {
string name;
int grade;
bool operator< (int grade){
return grade < grade;
}
bool operator< (string name){
return name < name;
}
};
void populateStudentRecords(vector<StudentInfo>Students,vector<int>::iterator iter, int x){
for(auto iter = Students.begin(); iter != Students.end(); ++iter){
iter->name = names[x];
iter->name.push_back(x);
iter->grade = x++;
x = x++;
}
}
bool sortByName(const StudentInfo x, const StudentInfo y){
return x.name < y.name;
}
bool sortByGrade(const StudentInfo x, const StudentInfo y){
return x.grade < y.grade;
}
void displayRecords(vector<StudentInfo>Records,vector<int>::iterator iter){
for(auto iter = Records.begin(); iter != Records.end(); ++iter){
cout<<*iter->name<<" -"<<*iter->grade<<endl;
}
}
void displayMaxAndMinGrade(vector<StudentInfo>Records,vector<int>::iterator iter){
for(auto iter = Records.begin(); iter != Records.end(); ++iter){
cout<<*iter->name<<" - " <<*iter->grade<<endl;
iter = Records.end();
cout<<*iter->name<<" - " <<*iter->grade<<endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<StudentInfo>Records (numberOfStudents);
vector<int>::iterator iter;
populateStudentRecords(Records,iter,0);
sort(Records.begin(),Records.end(),sortByName);
displayRecords(Records,iter);
sort(Records.begin(),Records.end(),sortByGrade);
cout<<" "<<endl;
displayMaxAndMinGrade(Records, iter);
return 0;
}
In the displayRecords function and the displayMaxAndMin function, I have the * symbol next to the iterator. I want the computer to display the value of these variables within each occurrence of the structure in the vector. However, I get an error c2100 when I try to build the program. I have tried to run the program without including the * symbol, but that displays the address of each variable and also leads to a crash. How do I fix this? Thank you.
You need to read an introductory book on programming with C++. This code is full of errors. You don't know how to use pointers/references/iterators.
Learn about copying, references, and pointers from a good book. Follow the link at the end of this answer.
Variables should be limited to the scope in which they are used. Stop passing so many arguments to your function when they don't really need them.
You are using both the indirection and dereference operators at the same time when you only need one for the type you are applying the operators to. This is the reason for your error.
You are incrementing an array index twice per loop, which makes it go out of bounds.
You are using your container's end() member function with the assumption that it returns an iterator to the last element in the container.
You want to print the lowest and highest grades but loop through the whole container.
For your sake, I've fixed the most glaring errors:
#include <iostream>
#include <list>
#include <cstdlib>
#include <time.h>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
const int numberOfStudents = 9;
string names[numberOfStudents] = { "Billy", "Abe","Carl","Dillan","Eddie","Felix","Gill","Herald","Isaac" };
struct StudentInfo
{
string name;
int grade;
};
void populateStudentRecords(vector<StudentInfo>& Students)
{
int x{ 0 };
for (auto iter = Students.begin(); iter != Students.end(); ++iter)
{
iter->name = names[x];
iter->name.push_back(x);
iter->grade = ++x;
}
}
bool sortByName(const StudentInfo x, const StudentInfo y)
{
return x.name < y.name;
}
bool sortByGrade(const StudentInfo x, const StudentInfo y)
{
return x.grade < y.grade;
}
void displayRecords(vector<StudentInfo>Records)
{
for (auto iter = Records.begin(); iter != Records.end(); ++iter)
{
cout << iter->name << " -" << iter->grade << endl;
}
}
void displayMaxAndMinGrade(vector<StudentInfo>Records)
{
auto iter = Records.begin();
cout << iter->name << " - " << iter->grade << endl;
iter = Records.end() - 1;
cout << iter->name << " - " << iter->grade << endl;
}
int main()
{
vector<StudentInfo>Records(numberOfStudents);
populateStudentRecords(Records);
sort(Records.begin(), Records.end(), sortByName);
displayRecords(Records);
sort(Records.begin(), Records.end(), sortByGrade);
cout << " " << endl;
displayMaxAndMinGrade(Records);
return 0;
}
Please do yourself a favor and visit the following link:
The Definitive C++ Book Guide and List

Displacing location while iterating through vector

How can you displace the location you iterate through a vector? I've tried something like:
for(auto x : vect+2)
but this doesn't work. I'm sure there's a simple resolve, but I haven't been able to find anything online.
If you want to use the range-based for, you could use Boost.Range to create a range that starts from the third element of your vector (begin() + 2):
for (auto x : boost::make_iterator_range(begin(v) + 2, end(v)))
{
std::cout << x << " ";
}
Here is a simple example:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
int main()
{
std::vector<int> v(10);
iota(begin(v), end(v), 1);
for (auto x : boost::make_iterator_range(begin(v) + 2, end(v)))
{
std::cout << x << " ";
}
}
If you want to loop through every second element, instead, you could change your range as follows:
namespace rng = boost::adaptors;
for (auto x : v | rng::strided(2))
{
std::cout << x << " ";
}
Which in a full program would be:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
int main()
{
namespace rng = boost::adaptors;
std::vector<int> v(10);
iota(begin(v), end(v), 1);
for (auto x : v | rng::strided(2))
{
std::cout << x << " ";
}
}
Boost.Range is pretty flexible, so you can for instance combine the two adapters above:
for (auto x : boost::make_iterator_range(begin(v) + 2, end(v)) |
rng::strided(3))
{
std::cout << x << " ";
}
If you do not want or cannot use Boost, you could use a classical for loop with iterators:
for (auto i = begin(v) + 2; i != end(v); ++i)
{
std::cout << *i << " ";
}
This is how the whole program would look like:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v(10);
iota(begin(v), end(v), 1);
for (auto i = begin(v) + 2; i != end(v); ++i)
{
std::cout << *i << " ";
}
}
This can be achieved very simply, and has a number of solutions to suit any programming style.
The Classical Approach
int main()
{
std::vector<int> v(10);
std::iota(v.begin(), v.end(), 1);
for (auto i = v.begin() + 2; i != v.end(); ++i)
{
std::cout << *i << " ";
}
}
The Functional Approach
int main()
{
std::vector<int> v(10);
std::iota(v.begin(), v.end(), 1);
std::for_each(v.begin() + 2, v.end(), [](int val)
{
std::cout << val << " ";
}
);
}
You could add a light wrapper to make it work with range-based for:
#include <iostream>
#include <vector>
#include <iterator>
namespace range
{
template <typename C>
struct make_range
{
C t;
make_range(C t, int offset)
: t(std::begin(t) + offset, std::end(t))
{}
auto begin() -> decltype(t.begin())
{
return t.begin();
}
auto end() -> decltype(t.end())
{
return t.end();
}
};
}
int main()
{
std::vector<int> v{1, 2, 3, 4, 5};
for (auto i : range::make_range<decltype(v)>(v, 2))
{
std::cout << i << std::endl;
}
}
Perhaps it would be better to just store the iterators:
namespace range
{
template <typename C>
struct make_range
{
typename C::iterator beg_iter;
typename C::iterator end_iter;
make_range(C& t, int offset)
: beg_iter(std::begin(t) + offset), end_iter(std::end(t))
{}
auto begin() -> decltype(beg_iter)
{
return beg_iter;
}
auto end() -> decltype(end_iter)
{
return end_iter;
}
};
}

Easier way to count identical strings in a vector?

I am trying to get an output of the number of all the identical strings in a vector as part of a much larger program. After a lot of research I have managed to put something together that works but it seems messy and I was wondering if there was a better way to do it.
#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;
void setMap(string i);
void addMap(string i);
map<string, int> myMap;
int main()
{
vector<string> myVector;
string myArray[6]={"foo","foo","bar","roo","foo","bar"};
for (int i=0; i<6; i++)
{
myVector.push_back(myArray[i]);
}
for_each (myVector.begin(), myVector.end(), setMap);
for_each (myVector.begin(), myVector.end(), addMap);
for (map<string, int, less< string >>::const_iterator iter = myMap.begin();
iter != myMap.end(); ++iter )
cout <<iter->first<<'\t'<<iter->second<<endl;
return 0;
}
void setMap(string i)
{
myMap[i]=0;
}
void addMap(string i)
{
myMap[i]++;
}
This code works fine and gives me the output I was after but I'm not that keen on having to add 2 extra functions to make it work or having to make the map global. Any hints would be gratefully received.
Well the simplest way to not have the extra functions and not have the map as global would be to not use for_each.
for_each (myVector.begin(), myVector.end(), setMap);
for_each (myVector.begin(), myVector.end(), addMap);
becomes
map<string, int> myMap;
for (vector<string>::iterator i = myVector.begin(); i != myVector.end(); ++i)
myMap[*i]=0;
for (vector<string>::iterator i = myVector.begin(); i != myVector.end(); ++i)
++myMap[*i];
Once you done that you could also remove the first loop
map<string, int> myMap;
for (vector<string>::iterator i = myVector.begin(); i != myVector.end(); ++i)
++myMap[*i];
since the map values will be initialised to zero anyway.
What made you think you had to use for_each anyway?
Your setMap function is unnecessary.
Consider what this function does, should the map's key not be present.
void addMap(string i)
{
myMap[i]++;
}
The expression myMap[i] will add a new key to your map.
Since the value type is int, this new value will be int(), which is guaranteed to be 0.
What about this? Encapsulate the counting mechanism in a separate function for reusability.
// Iterator pair based interface
template <class Iterator>
std::map<typename Iterator::value_type,int>
count(Iterator begin, Iterator end) {
std::map<typename Iterator::value_type,int> counts;
for (Iterator i = begin; i != end; ++i)
counts[*i]++;
return counts;
}
// Sequence interface
template <class Sequence>
inline std::map<typename Sequence::value_type,int>
count(Sequence seq) {
return count(seq.begin(), seq.end());
}
Then simply use it like this:
// C++11
for (const auto & c : count(myVector))
cout << c->first << '\t' << c->second << endl;
// C++03
std::map<string,int> counts = count(myVector);
for (std::map<string,int>::const_iterator c = counts.begin(), e = counts.end(); c != e; ++c)
cout << c->first << '\t' << c->second << endl;
Simple demo
Under C++11, you can do this:
#include <string>
#include <unordered_map>
#include <iostream>
int main() {
std::string myArray[6] = {"foo","foo","bar","roo","foo","bar"};
std::unordered_map<std::string, size_t> m;
for (const auto& s : myArray)
++m[s];
for (const auto& p : m)
std::cout << p.first << "\t" << p.second << std::endl;
}
This prints:
foo 3
bar 2
roo 1
This works because m[s] will automatically insert s into m if not already there.
Using std::unordered_map (a hashtable) is likely to be cheaper than std::map (a balanced tree).
You can do something very similar under C++03, except the "for each" loops shown above would be replaced by the regular "for" loops.
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <map>
using namespace std;
int main (int argc, char * const argv[]) {
string myArray[]={"foo","foo","bar","roo","foo","bar"};
int arr_length = 6;
vector<string> myVector(myArray, myArray + arr_length);
//Print contents of vector:
copy(myVector.begin(),
myVector.end(),
ostream_iterator<string>(cout, " ")
);
cout << endl;
map<string, int> myMap;
vector<string>::iterator pos;
for (pos=myVector.begin(); pos<myVector.end(); ++pos)
{
myMap[*pos] += 1;
}
map<string, int>::iterator mapPos;
for (mapPos=myMap.begin(); mapPos != myMap.end(); ++mapPos) {
cout << "word: " << mapPos->first << "\t"
<< "count: " << mapPos->second << endl;
}
return 0;
}
--output:--
foo foo bar roo foo bar
word: bar count: 2
word: foo count: 3
word: roo count: 1

Storing values from a vector to a string as comma seperated values

how can I store the values returned from a function to a string as comma seperated values. Can anyone help me..?
const myVector &vecList = obj.get_List();
vector<myNumVector *>::const_iterator iter;
for (iter= vecList.begin(); iter!= vecList.end(); iter++)
{
string myNum = (*iter)->get_myNum();
string myNumList = ?
//myNumList should be = drt123,ret34,dfghgd234.... if these are the return values
} //can we achive this by use of some sting functions..?
As can be seen from the links I posted, there are lots of ways to do this. Here is, I believe, the simplest:
#include <vector>
using std::vector;
#include <string>
using std::string;
#include <boost/assign/list_of.hpp>
using boost::assign::list_of;
namespace ba = boost::assign;
vector<string> victor = list_of
("Clarence Oveur")
("Roger Murdock")
("Victor Basta");
int main() {
string result;
for(vector<string>::iterator it = victor.begin();
it != victor.end();
++it) {
if(it != victor.begin()) {
result += ", ";
}
result += *it;
}
cout << result << "\n";
}
EDIT: To translate directly to OP's question:
const myVector &vecList = obj.get_List();
vector<myNumVector *>::const_iterator iter;
string myNumlist;
for (iter= vecList.begin(); iter!= vecList.end(); iter++)
{
string myNum = (*iter)->get_myNum();
if(iter!=vecList.begin()) {
nyNumList += ",";
}
myNumList += myNum;
}
EDIT: Simplified by removing bool first from previous solution.
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
int main () {
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
std::stringstream list;
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(list, ","));
std::cout << list.str();
return 0;
}
Outputs: 1,2,3,4,
more modern approach, also solving the trailing ","
#include <string>
#include <numeric>
#include <iostream>
int main() {
const auto v = {1, 2, 3, 4};
const auto list = std::accumulate(begin(v), end(v), std::string{}, [](const std::string& so_far, const auto& next) {
return so_far + (so_far.empty() ? "" : ", ") + std::to_string(next);
});
std::cout << list;
return 0;
}
Yes, this can be achieved using string functions, along with a handful other methods.
Given a string myNumList defined outside the loop, you could simply
myNumList += "," + myNum;
although that would add an extraneous comma in the beinning, so check if iter is pointing there first:
if(iter != vecList.begin())
myNumList += ',';
myNumList += myNum;