11
\$\begingroup\$

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)
\$\endgroup\$

2 Answers 2

5
\$\begingroup\$

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);
}
\$\endgroup\$
3
  • \$\begingroup\$ I think the two for loops are clearer than my two while loops. Having the next makes it clearer that the inner loop is over an increasingly smaller sub-list. \$\endgroup\$
    – Peter Wood
    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
    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 the for, but having thought about it I prefer it to while, now. \$\endgroup\$
    – Peter Wood
    Mar 2, 2013 at 23:02
5
\$\begingroup\$

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.

\$\endgroup\$
3
  • \$\begingroup\$ Maybe pairwise_combinations. \$\endgroup\$
    – Peter Wood
    Feb 28, 2013 at 12:30
  • 1
    \$\begingroup\$ Or combine_pairwise \$\endgroup\$
    – Peter Wood
    Feb 28, 2013 at 12:30
  • \$\begingroup\$ @PeterWood: Yes, those are decent names. \$\endgroup\$
    – Jan Hudec
    Feb 28, 2013 at 12:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.