I came up with this code whilst answering this question.
Is there a simpler way of doing this using standard library?
I want to iterate over every object and do something with every other object.
For example, 4 values 1
, 2
, 3
, 4
would pair like:
(1, 2), (1, 3), (1, 4)
(2, 3), (2, 4)
(3, 4)
Each value combines with every other value. None combine with themselves, and symmetric pairings are considered the same.
This might be useful in a collision system where you want to check every solid with every other.
template<typename Iter, typename Func>
void pair_wise(Iter it, Iter last, Func func) {
while(it != last) {
Iter other = it;
++other;
while(other != last) {
func(*it, *other);
++other;
}
++it;
}
}
Usage:
#include <iostream>
#include <vector>
int main() {
std::vector<int> values = {1, 2, 3, 4};
pair_wise(values.begin(), values.end(),
[](int& lhs, int& rhs) {
std::cout << "(" << lhs << ", " << rhs << ")\n";
});
}
Output:
(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
2 Answers 2
You could do this:
template<typename Iter, typename Func>
void combine_pairwise(Iter first, Iter last, Func func)
{
for(; first != last; ++first)
std::for_each(std::next(first), last, std::bind(func, *first, std::placeholders::_1));
}
but if I was doing this in real code I would opt not to. The above is basically just being complicated for the hell of it. I would write the following in real code:
template<typename Iter, typename Func>
void combine_pairwise(Iter first, Iter last, Func func)
{
for(; first != last; ++first)
for(Iter next = std::next(first); next != last; ++next)
func(*first, *next);
}
-
\$\begingroup\$ I think the two
for
loops are clearer than my twowhile
loops. Having thenext
makes it clearer that the inner loop is over an increasingly smaller sub-list. \$\endgroup\$Open AI - Opting Out– Open AI - Opting Out2013年03月01日 09:30:50 +00:00Commented Mar 1, 2013 at 9:30 -
\$\begingroup\$ @PeterWood I don't know why, but even seasoned C++ devs love to write
iter i = begin; while(i != end){ /*...*/ ++i; }
(or the equivalent) instead of using a for loop. I find it constantly in other people's code; And it's much harder to quickly understand what's going on (it's longer too). \$\endgroup\$David– David2013年03月02日 21:16:13 +00:00Commented Mar 2, 2013 at 21:16 -
\$\begingroup\$ Normally I would write a
for
loop, but as the iterators were passed in and no initialisation was necessary I just jumped straight to the condition:while
. It doesn't feel quite right to have empty initialisation in thefor
, but having thought about it I prefer it towhile
, now. \$\endgroup\$Open AI - Opting Out– Open AI - Opting Out2013年03月02日 23:02:41 +00:00Commented Mar 2, 2013 at 23:02
It would be possible to write as for_each
call to a functor writing for_each
again, but I don't think it would actually be shorter.
I don't think pair_wise
is a good name. There are two many things that it could mean. I'd suggest something with combinations
as it calls the function for all 2-combinations.
-
\$\begingroup\$ Maybe
pairwise_combinations
. \$\endgroup\$Open AI - Opting Out– Open AI - Opting Out2013年02月28日 12:30:01 +00:00Commented Feb 28, 2013 at 12:30 -
1\$\begingroup\$ Or
combine_pairwise
\$\endgroup\$Open AI - Opting Out– Open AI - Opting Out2013年02月28日 12:30:48 +00:00Commented Feb 28, 2013 at 12:30 -
\$\begingroup\$ @PeterWood: Yes, those are decent names. \$\endgroup\$Jan Hudec– Jan Hudec2013年02月28日 12:31:32 +00:00Commented Feb 28, 2013 at 12:31