C как отсортировать кортеж на основе первого, затем второго, затем третьего

#c

#c

Вопрос:

Код отсортирует std::pair на основе первых элементов, а затем второго (если первые элементы равны).

Кто-нибудь может показать мне, как я мог бы отсортировать на основе третьего элемента, используя кортеж? если первое и второе совпадают?

Что хотелось бы знать, так это как использовать предикат для сортировки на основе первого, затем второго (если первое равно), затем третьего (если первое и второе равны)?

Например, если бы у меня было:

 (3,2,3)
(1,1,0)
(1,1,1)
(2,2,2)
  

Это напечатало бы:

 (1,1,0)
(1,1,1)
(2,2,2)
(3,2,3)
  

Код:

 #include <iostream>
#include <vector>
#include <algorithm>
#include <utility>

typedef std::pair<double, double> my_pair;

struct sort_pred
{
    bool operator ()( const my_pair amp;left, const my_pair amp;right ) const
    {
        return ( left.first < right.first ) || 
               ( !( right.first < left.first ) amp;amp; ( right.second > left.second ) );
    }
};

int main()
{
    std::vector<my_pair> data;

    data.push_back( my_pair( 3, 2 ) );
    data.push_back( my_pair( 1, 2 ) );
    data.push_back( my_pair( 1, 1 ) );
    data.push_back( my_pair( 2, 2 ) );

    std::stable_sort( data.begin(), data.end(), sort_pred() );

    for ( const auto amp;p : data ) std::cout << p.first << ' ' << p.second << std::endl;
}  
  

Комментарии:

1. поместить их в std::tuple , а потом позвонить std::sort ?

2. Вы пробовали сортировать vector<tuple<int,int,int>> без предиката и посмотреть, что получится?

3. …нравится это ?

4. @TedLyngmo согласился. Похоже, вопрос нуждается в деталях или ясности.

5. @user12660947 Зачем вам вообще нужен предикат? std::pair и std::tuple уже operator< реализовано сравнение значений так, как вы описываете. Так что плохого в том, чтобы позволить std::sort() выполнять сортировку по умолчанию без использования пользовательского предиката?

Ответ №1:

Что хотелось бы знать, так это как использовать предикат для сортировки на основе первого, затем второго (если первое равно), затем третьего (если первое и второе равны)?

У std::tuple уже есть operator< , который выполняет вышеуказанное, но чтобы сделать это вручную, люди в основном используют для этого std::tie , который сам создает кортеж из предоставленных аргументов. Тогда функтор сравнения для класса с тремя полями ( f1 f3 ) мог бы быть:

 [](autoamp; a, autoamp; b) { return std::tie(a.f1, a.f2, a.f3) < std::tie(b.f1, b.f2, b.f3); }
  

Чтобы показать что-то другое, предположим, что вы хотите порядок сортировки по возрастанию для первых двух полей, но порядок убывания для последнего поля.

Обратите внимание, что я поменял местами a и b в std::get<2> :

 auto comp = [](autoamp; a, autoamp; b) {
    return
        std::tie(std::get<0>(a), std::get<1>(a), std::get<2>(b))
        <
        std::tie(std::get<0>(b), std::get<1>(b), std::get<2>(a));
};

std::sort(data.begin(), data.end(), comp);
  

Это дало бы вам такой приказ:

 1,1,1
1,1,0
2,2,2
3,2,3
  

Поменяйте местами a и b in std::get<2> обратно, и вы получите тот же порядок, что и в вашем исходном кортеже, который уже встроен.

Если вы хотите сделать это без помощи std::tie , вы могли бы сделать это следующим образом:

 struct sort_pred {
    bool operator ()(const my_tupleamp; left, const my_tupleamp; right) const {
        if(std::get<0>(left) != std::get<0>(right)) 
            return std::get<0>(left) < std::get<0>(right);

        if(std::get<1>(left) != std::get<1>(right))
            return std::get<1>(left) < std::get<1>(right);

        return std::get<2>(left) < std::get<2>(right);
    }
};
  

Или, если вы хотите, чтобы это было с большим количеством троичных операторов, вы тоже можете это сделать. Я нахожу это немного более трудным для чтения, но, думаю, это дело вкуса:

 struct sort_pred {
    bool operator ()(const my_tupleamp; left, const my_tupleamp; right) const {
        return 
            std::get<0>(left) != std::get<0>(right) ? std::get<0>(left) < std::get<0>(right) :
            std::get<1>(left) != std::get<1>(right) ? std::get<1>(left) < std::get<1>(right) :
            std::get<2>(left) < std::get<2>(right);
    }
};
  

Комментарии:

1. Альтернативное предложение: просто используйте std::tuple для начала вместо std::tie

2. @MooingDuck Да, я действительно не знаю, есть ли плюсы и минусы в том, чтобы делать это так или иначе. std::tie коротко, так что это бонус. 🙂