Chaining of ordering predicates (e.g. for std::sort)

前端 未结 6 1824
说谎
说谎 2021-02-05 23:33

You can pass a function pointer, function object (or boost lambda) to std::sort to define a strict weak ordering of the elements of the container you want sorted.

Howeve

相关标签:
6条回答
  • 2021-02-06 00:18

    The chaining solution is verbose. You could also use boost::bind in conjunction with std::logical_and to build your sorting predicate. See the linked article for more information: How the boost bind library can improve your C++ programs

    0 讨论(0)
  • 2021-02-06 00:21

    Variadic templates in C++ 11 give a shorter option:

        #include <iostream>
        using namespace std;
    
        struct vec { int x,y,z; };
    
        struct CmpX {
          bool operator() (const vec& lhs, const vec& rhs) const 
          { return lhs.x < rhs.x; }
        };
    
        struct CmpY {
          bool operator() (const vec& lhs, const vec& rhs) const 
          { return lhs.y < rhs.y; }
        };
    
        struct CmpZ {
          bool operator() (const vec& lhs, const vec& rhs) const 
          { return lhs.z < rhs.z; }
        };
    
        template <typename T>
        bool chained(const T &, const T &) {
          return false;
        }
    
        template <typename CMP, typename T, typename ...P>
        bool chained(const T &t1, const T &t2, const CMP &c, P...p) {
          if (c(t1,t2)) { return true;          }
          if (c(t2,t1)) { return false;         }
          else          { return chained(t1, t2, p...); }
        }
    
        int main(int argc, char **argv) {
          vec x = { 1,2,3 }, y = { 2,2,3 }, z = { 1,3,3 };
          cout << chained(x,x,CmpX(),CmpY(),CmpZ()) << endl;
          return 0;
        }
    
    0 讨论(0)
  • 2021-02-06 00:30

    std::sort is not guaranteed to be stable because stable sorts are usually slower than non-stable ones ... so using a stable sort multiple times looks like a recipe for performance trouble...

    And yes it's really a shame that sort ask for a predicate: I see no other way than create a functor accepting a vector of tristate functions ...

    0 讨论(0)
  • 2021-02-06 00:32

    You could build a little chaining system like so:

    struct Type {
      string first, last;
      int age;
    };
    
    struct CmpFirst {
      bool operator () (const Type& lhs, const Type& rhs) { return lhs.first < rhs.first; }
    };
    
    struct CmpLast {
      bool operator () (const Type& lhs, const Type& rhs) { return lhs.last < rhs.last; }
    };
    
    struct CmpAge {
      bool operator () (const Type& lhs, const Type& rhs) { return lhs.age < rhs.age; }
    };
    
    template <typename First, typename Second>
    struct Chain {
      Chain(const First& f_, const Second& s_): f(f_), s(s_) {}
    
      bool operator () (const Type& lhs, const Type& rhs) {
        if(f(lhs, rhs))
          return true;
        if(f(rhs, lhs))
          return false;
    
        return s(lhs, rhs);
      }
    
      template <typename Next>
      Chain <Chain, Next> chain(const Next& next) const {
         return Chain <Chain, Next> (*this, next);
      }
    
      First f;
      Second s;
    };
    
    struct False { bool operator() (const Type& lhs, const Type& rhs) { return false; } };
    
    template <typename Op>
    Chain <False, Op> make_chain(const Op& op) { return Chain <False, Op> (False(), op); }
    

    Then to use it:

    vector <Type> v;  // fill this baby up
    
    sort(v.begin(), v.end(), make_chain(CmpLast()).chain(CmpFirst()).chain(CmpAge()));
    

    The last line is a little verbose, but I think it's clear what's intended.

    0 讨论(0)
  • 2021-02-06 00:32

    One conventional way to handle this is to sort in multiple passes and use a stable sort. Notice that std::sort is generally not stable. However, there’s std::stable_sort.

    That said, I would write a wrapper around functors that return a tristate (representing less, equals, greater).

    0 讨论(0)
  • 2021-02-06 00:34

    You can try this:

    Usage:

    struct Citizen {
        std::wstring iFirstName;
        std::wstring iLastName;
    };
    
    ChainComparer<Citizen> cmp;
    cmp.Chain<std::less>( boost::bind( &Citizen::iLastName, _1 ) );
    cmp.Chain<std::less>( boost::bind( &Citizen::iFirstName, _1 ) );
    
    std::vector<Citizen> vec;
    std::sort( vec.begin(), vec.end(), cmp );
    

    Implementation:

    template <typename T>
    class ChainComparer {
    public:
    
        typedef boost::function<bool(const T&, const T&)> TComparator;
        typedef TComparator EqualComparator;
        typedef TComparator CustomComparator;
    
        template <template <typename> class TComparer, typename TValueGetter>
        void Chain( const TValueGetter& getter ) {
    
            iComparers.push_back( std::make_pair( 
                boost::bind( getter, _1 ) == boost::bind( getter, _2 ), 
                boost::bind( TComparer<TValueGetter::result_type>(), boost::bind( getter, _1 ), boost::bind( getter, _2 ) ) 
            ) );
        }
    
        bool operator()( const T& lhs, const T& rhs ) {
            BOOST_FOREACH( const auto& comparer, iComparers ) {
                if( !comparer.first( lhs, rhs ) ) {
                    return comparer.second( lhs, rhs );
                }
            }
    
            return false;
        }
    
    private:
        std::vector<std::pair<EqualComparator, CustomComparator>> iComparers;
    };
    
    0 讨论(0)
提交回复
热议问题