Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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)
share|improve this question

2 Answers

up vote 3 down vote accepted

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);
}
share|improve this answer
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. – Peter Wood Mar 1 at 9:30
@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). – Dave Mar 2 at 21:16
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. – Peter Wood Mar 2 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.

share|improve this answer
Maybe pairwise_combinations. – Peter Wood Feb 28 at 12:30
1  
Or combine_pairwise – Peter Wood Feb 28 at 12:30
@PeterWood: Yes, those are decent names. – Jan Hudec Feb 28 at 12:31

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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