my function looks like this:
bool getPair(std::vector<std::vector<unsigned short>>Cards) {
std::sort(Cards.begin(), Cards.end(), Cardsort);
std::map<unsigned short, int>Counter;
for (int i = 0; i < 6; i++)
Counter[Cards[i][0]];
for (const auto& val : Counter) {
if (val.second == 2)
return true;
}
return false;
}
I'm pretty sure I'm using std::map incorrectly, I basically have the vector setup like so:
{{2,0},{3,0},{4,1},{3,0},{4,0},{5,0},{6,0}}
where the first number represents value, the second represents card suit. I realize now I should have used an object which may have made this problem less complicated but now I'm trying to use std::map to count how many times the value shows up and if it shows up two times, it show return true (in the vector it would return true at the 3) but I don't think I'm using std::map properly
I want to see if Cards has more than one of the same variable in Cards[i][0], I do not care about duplicates in Cards[i][1]
Tested this and works. Highlighted the fix
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
bool getPair(std::vector<std::vector<unsigned short>>Cards) {
std::sort(Cards.begin(), Cards.end());
std::map<unsigned short, int>Counter;
for (int i = 0; i < 6; i++)
Counter[Cards[i][0]]++; // ++++++++++++++++++ need to alter the value!
for (const auto& val : Counter) {
if (val.second == 2)
return true;
}
return false;
}
int main() {
// your code goes here
// {{2,0},{3,0},{4,1},{3,0},{4,0},{5,0},{6,0}}
std::vector<std::vector<unsigned short>> c = {{2,0},{3,0},{4,1},{3,0},{4,0},{5,0},{6,0}};
std::cout << getPair(c);
return 0;
}
Here´s my suggestion.
Some remarks:
why use two loops? You already have the map entry to check, since you want to increase it, so you can check for doubles aka pairs in the counting loop. No need for a second run. This way it´s much less expensive.
I changed the vector parameter to const&. It´s a very bad idea to pass such a thing by value, at least I can´t see why that could be appropriate in that case
I left out the sorting thingy, can´t see for what end it´s needed, just reinsert it, if necessary. Sorting is very expensive.
you are right in the fact that std:: containers do not need initialization, they are proper initialized, the allocator calls the constructor of new elements, event for e.g. int thats one reason why e.g. int got a default constructor syntax and you can write funny thingies like auto a = int();.
accessing nonexistent keys of a map simply creates them
using a set and counting will definitely not yield better performance
I think the code is pretty easy to read, here you are:
#include <iostream>
#include <vector>
#include <map>
bool getPair(const std::vector<std::vector<unsigned short>>& cards) {
std::map<unsigned short, int> counts;
for(const auto& n : cards) {
if(++counts[n[0]] == 2)
return true;
}
return false;
}
int main()
{
std::vector<std::vector<unsigned short>> cards1 = {{2,0},{3,0},{4,1},{3,0},{4,0},{5,0},{6,0}};
std::vector<std::vector<unsigned short>> cards2 = {{1,0},{2,0},{4,1},{3,0},{5,0},{7,0},{6,0}};
std::cout << getPair(cards1) << "\n";
std::cout << getPair(cards2) << "\n";
return 0;
}
Edit:
Quote of the C++14 Standard regarding access to not existing members of std::map, just for the sake of completeness:
23.4.4.3 map element access [map.access]
T& operator[](const key_type& x);
Effects: If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map.
Requires: key_type shall be CopyInsertable and mapped_type shall be DefaultInsertable into
*this.
Returns: A reference to the mapped_type corresponding to x in *this.
Complexity: Logarithmic.23.4.4.3 map element access
First, you address uninitialized variables in Counter, and then you don't really do anything with it (and why do you run till 6 instead of Cards.size()? Your array has size 7 BTW. Also why there is some kind of sort there? You don't need it.):
std::map<unsigned short, int>Counter;
for (int i = 0; i < 6; i++)
Counter[Cards[i][0]];
They might set the uninitialized variable automatically at 0 or they might not - it depends on the implementation as it is not specified as far as I am aware (in Debug they do set it to 0 but I doubt about the Release version). You'll need to rewrite the code as follows to make it work 100% in all circumstances:
std::map<unsigned short, int> Counter;
for (int i = 0; i < (int)Cards.size(); i++)
{
unsigned short card = Cards[i][0];
auto itr = Counter.find(card);
if(itr == Counter.end())
Counter[card] = 1;
else
itr->second++;
}
I would recommend to use std::set for this task:
std::set<unsigned short> Counter;
for (int i = 0; i < (int)Cards.size(); i++)
{
unsigned short card = Cards[i][0];
if(Counter.count(card)>0)
{
return true;
}
Counter.insert(card);
}
return false;
Related
I am a mathematician by training and need to simulate a continuous time Markov chain. I need to use a variant of Gillespie algorithm which relies on fast reading and writing to a 13-dimensional array. At the same time, I need to set the size of each dimension based on users input (they will be each roughly of order 10). Once these sizes are set by the user, they will not change throughout the runtime. The only thing which changes will be the data contained in them. What is the most efficient way of doing this?
My first try was to use the standard arrays but their sizes must be known at the compilation time, which is not my case. Is std::vector a good structure for this? If so, how shall I go about initializing a creature as:
vector<vector<vector<vector<vector<vector<vector<vector<vector<vector<vector<vector<vector<int>>>>>>>>>>>>> Array;
Will the initialization take more time than dealing with an array? Or, is there a better data container to use, please?
Thank you for any help!
I would start by using a std::unordered_map to hold key-value pairs, with each key being a 13-dimensional std::array, and each value being an int (or whatever datatype is appropriate), like this:
#include <iostream>
#include <unordered_map>
#include <array>
typedef std::array<int, 13> MarkovAddress;
// Define a hasher that std::unordered_map can use
// to compute a hash value for a MarkovAddress
// borrowed from: https://codereview.stackexchange.com/a/172095/126857
template<class T, size_t N>
struct std::hash<std::array<T, N>> {
size_t operator() (const std::array<T, N>& key) const {
std::hash<T> hasher;
size_t result = 0;
for(size_t i = 0; i < N; ++i) {
result = result * 31 + hasher(key[i]); // ??
}
return result;
}
};
int main(int, char **)
{
std::unordered_map<MarkovAddress, int> map;
// Just for testing
const MarkovAddress a{{1,2,3,4,5,6,7,8,9,10,11,12,13}};
// Place a value into the map at the specified address
map[a] = 12345;
// Now let's see if the value is present in the map,
// and retrieve it if so
if (map.count(a) > 0)
{
std::cout << "Value in map is " << map[a] << std::endl;
}
else std::cout << "Value not found!?" << std::endl;
return 0;
}
That will give you fast (O(1)) lookup and insert, which is likely your first priority. If you later run into trouble with that (e.g. too much RAM used, or you need a well-defined iteration order, or etc) you could replace it with something more elaborate later.
I would like to parallelize a for loop in which values of an unordered_map are updated:
unordered_map<string,double> umap {{"foo", 0}, {"bar", 0}};
#pragma omp parallel for reduction(my_reduction:umap)
for (int i = 0; i < 100; ++i)
{
// some_string(i) would return either "foo" or "bar"
umap[some_string(i)] += some_double(i);
}
So, no new entries in the unordered_map would be created, only the existing entries would be updated with a sum.
In this answer a user declared reduction is defined for the case of a vector. Could a user declared reduction be similarly defined in the case of the unordered_map?
It can be done using a similar approach to the one taken in the answer you linked. The one issue we face is that std::transform uses an unfortunate line when it comes to maps.
//GCC version, but the documentation suggests the same thing.
*__result = __binary_op(*__first1, *__first2);
Since maps store types std::pair<const T1, T2> (i.e. the first must always be const, you can't modify the key), this would cause an error, because the operator= is deleted for this case.
For that reason we end up having to write this whole thing ourselves (the answer that follows could be made cleaner, I just hard-coded your type...).
We could just start with the examples of std::transform (look at example implementation 2) and modify the problematic part, but #Zulan raises a good point in the comments that simultaneously traversing unordered maps might not be a good idea (as they are, by definition, not ordered). While it might make some sense that the copy constructor retain the order, it seems that this is not guaranteed by the standard (at least I couldn't find it anywhere), as such the approach that std::transform takes becomes pretty useless.
We can resolve this issue with a slightly different reduction.
#include <unordered_map>
#include <string>
#include <iostream>
#include <utility>
void reduce_umaps(\
std::unordered_map<std::string, double>& output, \
std::unordered_map<std::string, double>& input)
{
for (auto& X : input) {
output.at(X.first) += X.second; //Will throw if X.first doesn't exist in output.
}
}
#pragma omp declare reduction(umap_reduction : \
std::unordered_map<std::string, double> : \
reduce_umaps(omp_out, omp_in)) \
initializer(omp_priv(omp_orig))
using namespace std;
unordered_map<string, double> umap {{"foo", 0}, {"bar", 0}};
string some_string(int in) {
if (in % 2 == 0) return "foo";
else return "bar";
}
inline double some_double(int in) {
return static_cast<double>(in);
}
int main(void) {
#pragma omp parallel for reduction(umap_reduction:umap)
for (int i = 0; i < 100; ++i) {
umap.at(some_string(i)) += some_double(i);
}
std::cerr << umap["foo"] << " " << umap["bar"] << "\n";
return 0;
}
You can also generalise this to allow the addition of keys within the parallel loop, but that won't parallelise well unless the number of added keys remains much smaller than the number of times you increase the values.
As a final side note, I replaced umap[some_string(i)] with umap.at(some_string(i)), to avoid accidentally adding elements much like was suggested in the comments, but find isn't the most practical function for that purpose.
I wrote the following code for constructing std::pair as key to unordered_map. However, I dont know why I am getting all 0's as output of vector. Can someone please suggest as to where am I going wrong?
struct key_hash
{
size_t operator()(const std::pair<unsigned,unsigned>& key) const
{
return uint64_t((key.first << 32) | key.second);
}
};
typedef std::unordered_map<std::pair<unsigned,unsigned>, std::vector<unsigned>, key_hash> MyMap;
int main()
{
MyMap m;
vector<unsigned> t;
t.push_back(4);
t.push_back(5);
m[make_pair(4294967292,4294967291)]=t;
for(vector<unsigned>::iterator i=m[make_pair(4294967292,4294967291)].begin(),j=m[make_pair(2147483645,2147483643)].end();i!=j;++i)
cout<<"vec="<<(*i)<<"\n";
cout<<"vector empty. \n";
}
i and j are iterators to 2 different vector's and they cannot be compared. Using debug iterators might catch this under visual studio.
This code: j=m[make_pair(2147483645,2147483643)].end(); will create a new empty vector since the key is different from the previously used one.
Whem initializing j like this: j=m[make_pair(4294967292,4294967291)].end(); the results are fine:
vec=4
vec=5
vector empty.
You are getting undefined behaviour, since m[make_pair(4294967292,4294967291)] and m[make_pair(2147483645,2147483643)] are probably different objects (unless something very strange with overflow wrapping is happening).
You may have a typo with the literals - just do key = make_pair(...u,...u).
Your hash functor probably has an overflow - unsigned int is probably 32-bit on your system).
Your literals probably exceed the maximum value of signed integers, and are not specified to be unsigned.
try changing your hash function to something along those lines:
struct key_hash
{
size_t operator()(const std::pair<unsigned,unsigned>& key) const
{
uint64_t tmp = key.first;
tmp = tmp << 32;
return uint64_t(tmp | key.second);
}
};
I have also added the one instance of the pair so changed the main to :
MyMap m;
vector<unsigned> t;
t.push_back(4);
t.push_back(5);
auto a = make_pair(4294967292,4294967291);
m[a]=t;
for(vector<unsigned>::iterator i=m[a].begin(),j=m[a].end();i!=j;++i)
cout<<"vec="<<(*i)<<"\n";
cout<<"vector empty. \n";
This gave me the correct output:
vec=4
vec=5
vector empty.
Tidying up the loop to make it clear (and ignoring the warning about a 32-bit left-shift on a 32-bit value...)
What you are doing is this:
const auto& first_vector = m[make_pair(4294967292,4294967291)];
const auto& second_vector = m[make_pair(2147483645,2147483643)];
for(auto iter = begin(first_vector) ;
iter != end(second_vector) ; // <<=== SEE THE PROBLEM?
++iter)
{
// ...
}
Incrementing the iterator of one vector will never yield the end() of a different one so your loop is infinite, until you get a segfault because you've accessed memory that does not belong to you.
I work with a lot of calculation code written in c++ with high-performance and low memory overhead in mind. It uses STL containers (mostly std::vector) a lot, and iterates over that containers almost in every single function.
The iterating code looks like this:
for (int i = 0; i < things.size(); ++i)
{
// ...
}
But it produces the signed/unsigned mismatch warning (C4018 in Visual Studio).
Replacing int with some unsigned type is a problem because we frequently use OpenMP pragmas, and it requires the counter to be int.
I'm about to suppress the (hundreds of) warnings, but I'm afraid I've missed some elegant solution to the problem.
On iterators. I think iterators are great when applied in appropriate places. The code I'm working with will never change random-access containers into std::list or something (so iterating with int i is already container agnostic), and will always need the current index. And all the additional code you need to type (iterator itself and the index) just complicates matters and obfuscates the simplicity of the underlying code.
It's all in your things.size() type. It isn't int, but size_t (it exists in C++, not in C) which equals to some "usual" unsigned type, i.e. unsigned int for x86_32.
Operator "less" (<) cannot be applied to two operands of different sign. There's just no such opcodes, and standard doesn't specify, whether compiler can make implicit sign conversion. So it just treats signed number as unsigned and emits that warning.
It would be correct to write it like
for (size_t i = 0; i < things.size(); ++i) { /**/ }
or even faster
for (size_t i = 0, ilen = things.size(); i < ilen; ++i) { /**/ }
Ideally, I would use a construct like this instead:
for (std::vector<your_type>::const_iterator i = things.begin(); i != things.end(); ++i)
{
// if you ever need the distance, you may call std::distance
// it won't cause any overhead because the compiler will likely optimize the call
size_t distance = std::distance(things.begin(), i);
}
This a has the neat advantage that your code suddenly becomes container agnostic.
And regarding your problem, if some library you use requires you to use int where an unsigned int would better fit, their API is messy. Anyway, if you are sure that those int are always positive, you may just do:
int int_distance = static_cast<int>(distance);
Which will specify clearly your intent to the compiler: it won't bug you with warnings anymore.
If you can't/won't use iterators and if you can't/won't use std::size_t for the loop index, make a .size() to int conversion function that documents the assumption and does the conversion explicitly to silence the compiler warning.
#include <cassert>
#include <cstddef>
#include <limits>
// When using int loop indexes, use size_as_int(container) instead of
// container.size() in order to document the inherent assumption that the size
// of the container can be represented by an int.
template <typename ContainerType>
/* constexpr */ int size_as_int(const ContainerType &c) {
const auto size = c.size(); // if no auto, use `typename ContainerType::size_type`
assert(size <= static_cast<std::size_t>(std::numeric_limits<int>::max()));
return static_cast<int>(size);
}
Then you write your loops like this:
for (int i = 0; i < size_as_int(things); ++i) { ... }
The instantiation of this function template will almost certainly be inlined. In debug builds, the assumption will be checked. In release builds, it won't be and the code will be as fast as if you called size() directly. Neither version will produce a compiler warning, and it's only a slight modification to the idiomatic loop.
If you want to catch assumption failures in the release version as well, you can replace the assertion with an if statement that throws something like std::out_of_range("container size exceeds range of int").
Note that this solves both the signed/unsigned comparison as well as the potential sizeof(int) != sizeof(Container::size_type) problem. You can leave all your warnings enabled and use them to catch real bugs in other parts of your code.
You can use:
size_t type, to remove warning messages
iterators + distance (like are first hint)
only iterators
function object
For example:
// simple class who output his value
class ConsoleOutput
{
public:
ConsoleOutput(int value):m_value(value) { }
int Value() const { return m_value; }
private:
int m_value;
};
// functional object
class Predicat
{
public:
void operator()(ConsoleOutput const& item)
{
std::cout << item.Value() << std::endl;
}
};
void main()
{
// fill list
std::vector<ConsoleOutput> list;
list.push_back(ConsoleOutput(1));
list.push_back(ConsoleOutput(8));
// 1) using size_t
for (size_t i = 0; i < list.size(); ++i)
{
std::cout << list.at(i).Value() << std::endl;
}
// 2) iterators + distance, for std::distance only non const iterators
std::vector<ConsoleOutput>::iterator itDistance = list.begin(), endDistance = list.end();
for ( ; itDistance != endDistance; ++itDistance)
{
// int or size_t
int const position = static_cast<int>(std::distance(list.begin(), itDistance));
std::cout << list.at(position).Value() << std::endl;
}
// 3) iterators
std::vector<ConsoleOutput>::const_iterator it = list.begin(), end = list.end();
for ( ; it != end; ++it)
{
std::cout << (*it).Value() << std::endl;
}
// 4) functional objects
std::for_each(list.begin(), list.end(), Predicat());
}
C++20 has now std::cmp_less
In c++20, we have the standard constexpr functions
std::cmp_equal
std::cmp_not_equal
std::cmp_less
std::cmp_greater
std::cmp_less_equal
std::cmp_greater_equal
added in the <utility> header, exactly for this kind of scenarios.
Compare the values of two integers t and u. Unlike builtin comparison operators, negative signed integers always compare less than (and not equal to) unsigned integers: the comparison is safe against lossy integer conversion.
That means, if (due to some wired reasons) one must use the i as integer, the loops, and needs to compare with the unsigned integer, that can be done:
#include <utility> // std::cmp_less
for (int i = 0; std::cmp_less(i, things.size()); ++i)
{
// ...
}
This also covers the case, if we mistakenly static_cast the -1 (i.e. int)to unsigned int. That means, the following will not give you an error:
static_assert(1u < -1);
But the usage of std::cmp_less will
static_assert(std::cmp_less(1u, -1)); // error
I can also propose following solution for C++11.
for (auto p = 0U; p < sys.size(); p++) {
}
(C++ is not smart enough for auto p = 0, so I have to put p = 0U....)
I will give you a better idea
for(decltype(things.size()) i = 0; i < things.size(); i++){
//...
}
decltype is
Inspects the declared type of an entity or the type and value category
of an expression.
So, It deduces type of things.size() and i will be a type as same as things.size(). So,
i < things.size() will be executed without any warning
I had a similar problem. Using size_t was not working. I tried the other one which worked for me. (as below)
for(int i = things.size()-1;i>=0;i--)
{
//...
}
I would just do
int pnSize = primeNumber.size();
for (int i = 0; i < pnSize; i++)
cout << primeNumber[i] << ' ';
Is there C++ equivalent for python Xrange generator in either STL or boost?
xrange basically generates incremented number with each call to ++ operator.
the constructor is like this:
xrange(first, last, increment)
was hoping to do something like this using boost for each:
foreach(int i, xrange(N))
I. am aware of the for loop. in my opinion they are too much boilerplate.
Thanks
my reasons:
my main reason for wanting to do so is because i use speech to text software, and programming loop usual way is difficult, even if using code completion. It is much more efficient to have pronounceable constructs.
many loops start with zero and increment by one, which is default for range. I find python construct more intuitive
for(int i = 0; i < N; ++i)
foreach(int i, range(N))
functions which need to take range as argument:
Function(int start, int and, int inc);
function(xrange r);
I understand differences between languages, however if a particular construct in python is very useful for me and can be implemented efficiently in C++, I do not see a reason not to use it. For each construct is foreign to C++ as well however people use it.
I put my implementation at the bottom of the page as well the example usage.
in my domain i work with multidimensional arrays, often rank 4 tensor. so I would often end up with 4 nested loops with different ranges/increments to compute normalization, indexes, etc. those are not necessarily performance loops, and I am more concerned with correctness readability and ability to modify.
for example
int function(int ifirst, int ilast, int jfirst, int jlast, ...);
versus
int function(range irange, range jrange, ...);
In the above, if different strids are needed, you have to pass more variables, modify loops, etc. eventually you end up with a mass of integers/nearly identical loops.
foreach and range solve my problem exactly. familiarity to average C++ programmer is not high on my list of concerns - problem domain is a rather obscure, there is a lot of meta-programming, SSE intrinsic, generated code.
Boost irange should really be the answer (ThxPaul Brannan)
I'm adding my answer to provide a compelling example of very valid use-cases that are not served well by manual looping:
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/irange.hpp>
using namespace boost::adaptors;
static int mod7(int v)
{ return v % 7; }
int main()
{
std::vector<int> v;
boost::copy(
boost::irange(1,100) | transformed(mod7),
std::back_inserter(v));
boost::sort(v);
boost::copy(
v | reversed | uniqued,
std::ostream_iterator<int>(std::cout, ", "));
}
Output: 6, 5, 4, 3, 2, 1, 0,
Note how this resembles generators/comprehensions (functional languages) and enumerables (C#)
Update I just thought I'd mention the following (highly inflexible) idiom that C++11 allows:
for (int x : {1,2,3,4,5,6,7})
std::cout << x << std::endl;
of course you could marry it with irange:
for (int x : boost::irange(1,8))
std::cout << x << std::endl;
Boost has counting_iterator as far as I know, which seems to allow only incrementing in steps of 1. For full xrange functionality you might need to implement a similar iterator yourself.
All in all it could look like this (edit: added an iterator for the third overload of xrange, to play around with boost's iterator facade):
#include <iostream>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/foreach.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <cassert>
template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T to)
{
//these assertions are somewhat problematic:
//might produce warnings, if T is unsigned
assert(T() <= to);
return boost::make_iterator_range(boost::counting_iterator<T>(0), boost::counting_iterator<T>(to));
}
template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T from, T to)
{
assert(from <= to);
return boost::make_iterator_range(boost::counting_iterator<T>(from), boost::counting_iterator<T>(to));
}
//iterator that can do increments in steps (positive and negative)
template <class T>
class xrange_iterator:
public boost::iterator_facade<xrange_iterator<T>, const T, std::forward_iterator_tag>
{
T value, incr;
public:
xrange_iterator(T value, T incr = T()): value(value), incr(incr) {}
private:
friend class boost::iterator_core_access;
void increment() { value += incr; }
bool equal(const xrange_iterator& other) const
{
//this is probably somewhat problematic, assuming that the "end iterator"
//is always the right-hand value?
return (incr >= 0 && value >= other.value) || (incr < 0 && value <= other.value);
}
const T& dereference() const { return value; }
};
template <class T>
boost::iterator_range<xrange_iterator<T> > xrange(T from, T to, T increment)
{
assert((increment >= T() && from <= to) || (increment < T() && from >= to));
return boost::make_iterator_range(xrange_iterator<T>(from, increment), xrange_iterator<T>(to));
}
int main()
{
BOOST_FOREACH(int i, xrange(10)) {
std::cout << i << ' ';
}
BOOST_FOREACH(int i, xrange(10, 20)) {
std::cout << i << ' ';
}
std::cout << '\n';
BOOST_FOREACH(int i, xrange(0, 46, 5)) {
std::cout << i << ' ';
}
BOOST_FOREACH(int i, xrange(10, 0, -1)) {
std::cout << i << ' ';
}
}
As others are saying, I don't see this buying you much over a normal for loop.
std::iota (not yet standardized) is kinda like range. Doesn't make things any shorter or clearer than an explicit for loop, though.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
int main() {
std::vector<int> nums(5);
std::iota(nums.begin(), nums.end(), 1);
std::copy(nums.begin(), nums.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
return 0;
}
Compile with g++ -std=c++0x; this prints "1 2 3 4 5 \n".
well, here is what i wrote, since there does not seem to be one.
the generator does not use any internal storage besides single integer.
range object can be passed around and used in nested loops.
there is a small test case.
#include "iostream"
#include "foreach.hpp"
#include "boost/iterator/iterator_categories.hpp"
struct range {
struct iterator_type {
typedef int value_type;
typedef int difference_type;
typedef boost::single_pass_traversal_tag iterator_category;
typedef const value_type* pointer;
typedef const value_type & reference;
mutable value_type value;
const difference_type increment;
iterator_type(value_type value, difference_type increment = 0)
: value(value), increment(increment) {}
bool operator==(const iterator_type &rhs) const {
return value >= rhs.value;
}
value_type operator++() const { return value += increment; }
operator pointer() const { return &value; }
};
typedef iterator_type iterator;
typedef const iterator_type const_iterator;
int first_, last_, increment_;
range(int last) : first_(0), last_(last), increment_(1) {}
range(int first, int last, int increment = 1)
: first_(first), last_(last), increment_(increment) {}
iterator begin() const {return iterator(first_, increment_);}
iterator end() const {return iterator(last_);}
};
int test(const range & range0, const range & range1){
foreach(int i, range0) {
foreach(int j, range1) {
std::cout << i << " " << j << "\n";
}
}
}
int main() {
test(range(6), range(3, 10, 3));
}
my main reason for wanting to do so is because i use speech to text software, and programming loop usual way is difficult, even if using code completion. It is much more efficient to have pronounceable constructs.
That makes sense. But couldn't a simple macro solve this problem? #define for_i_to(N, body) for (int i = 0; i < N; ++i) { body }
or something similar. Or avoid the loop entirely and use the standard library algorithms. (std::for_each(range.begin(), rang.end(), myfunctor()) seems easier to pronounce)
many loops start with zero and increment by one, which is default for range. I find python construct more intuitive
You're wrong. The Python version is more intuitive to a Python programmer. And it may be more intuitive to a non-programmer. But you're writing C++ code. Your goal should be to make it intuitive to a C++ programmer. And C++ programmer know for-loops and they know the standard library algorithms. Stick to using those. (Or stick to writing Python)
functions which need to take range as argument:
Function(int start, int and, int inc);
function(xrange r);
Or the idiomatic C++ version:
template <typename iter_type>
void function(iter_type first, iter_type last);
In C++, ranges are represented by iterator pairs. Not integers.
If you're going to write code in a new language, respect the conventions of that language. Even if it means you have to adapt and change some habits.
If you're not willing to do that, stick with the language you know.
Trying to turn language X into language Y is always the wrong thing to do. It own't work, and it'll confuse the language X programmers who are going to maintain (or just read) your code.
Since I've started to use BOOST_FOREACH for all my iteration (probably a misguided idea, but that's another story), here's another use for aaa's range class:
std::vector<int> vec;
// ... fill the vector ...
BOOST_FOREACH(size_t idx, make_range(0, vec.size()))
{
// ... do some stuff ...
}
(yes, range should be templatized so I can use user-defined integral types with it)
And here's make_range():
template<typename T>
range<T> make_range(T const & start, T const & end)
{
return range<T>(start, end);
}
See also:
http://groups.google.com/group/boost-list/browse_thread/thread/3e11117be9639bd
and:
https://svn.boost.org/trac/boost/ticket/3469
which propose similar solutions.
And I've just found boost::integer_range; with the above example, the code would look like:
using namespace boost;
std::vector<int> vec;
// ... fill the vector ...
BOOST_FOREACH(size_t idx, make_integer_range(0, vec.size()))
{
// ... do some stuff ...
}
C++ 20's ranges header has iota_view which does this:
#include <ranges>
#include <vector>
#include <iostream>
int main()
{
for (int i : std::views::iota{1, 10})
std::cout << i << ' ';
std::cout << '\n';
for (int i : std::views::iota(1) | std::views::take(9))
std::cout << i << ' ';
}
Output:
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
Since we don't really know what you actually want to use this for, I'm assuming your test case is representative. And then plain simple for loops are a whole lot simpler and more readable:
int main() {
for (int i = 0; i <= 6; ++i){
for (int j = 3; j <= 10; j += 3){
std::cout << i << " " << j << "\n";
}
}
}
A C++ programmer can walk in from the street and understand this function without having to look up complex classes elsewhere. And it's 5 lines instead of your 60. Of course if you have 400 loops exactly like these, then yes, you'd save some effort by using your range object. Or you could just wrap these two loops inside a helper function, and call that whenever you needed.
We don't really have enough information to say what's wrong with simple for loops, or what would be a suitable replacement. The loops here solve your problem with far less complexity and far fewer lines of code than your sample implementation. If this is a bad solution, tell us your requirements (as in what problem you need to solve, rather than "I want python-style loops in C++")
Keep it simple, make a stupid macro;
#define for_range(VARNAME, START, STOP, INCREMENT) \
for(int VARNAME = START, int STOP_ = STOP, INCREMENT_ = INCREMENT; VARNAME != STOP_; VARNAME += INCREMENT_)
and use as;
for_range(i, 10, 5, -1)
cout << i << endl;
You're trying to bring a python idiom into C++. That's unncessary. Use
for(int i=initVal;i<range;i+=increment)
{
/*loop body*/
}
to achieve this. In Python, the for(i in xrange(init, rng, increment)) form is necessary because Python doesn't provide a simple for loop, only a for-each type construct. So you can iterate only over a sequence or a generator. This is simply unnecessary and almost certainly bad practice in a language that provides a for(;;) syntax.
EDIT: As a completely non-recommended aside, the closest I can get to the for i xrange(first, last, inc) syntax in C++ is:
#include <cstdio>
using namespace std;
int xrange(unsigned int last, unsigned int first=0, unsigned int inc=1)
{
static int i = first;
return (i<last)?i+=inc:i=0;
}
int main()
{
while(int i=xrange(10, 0, 1))
printf("in loop at i=%d\n",i);
}
Not that while this loops the correct number of times, i varies from first+inc to last and NOT first to last-inc as in Python. Also, the function can only work reliably with unsigned values, as when i==0, the while loop will exit. Do not use this function. I only added this code here to demonstrate that something of the sort is indeed possible. There are also several other caveats and gotchas (the code won't really work for first!=0 on subsequent function calls, for example)