Как вы структурируете свои функции сравнения? - PullRequest
4 голосов
/ 28 февраля 2009

Я часто сталкиваюсь с ситуациями, особенно с сортировкой в ​​C ++, где я сравниваю ряд полей для сравнения большей структуры. Упрощенный пример:

struct Car{
    Manufacturer make;
    ModelName model;
    Year year;
};

bool carLessThanComparator( const Car & car1, const Car & car2 ){
    if( car1.make < car2.make ){
        return true;
    }else if( car1.make == car2.make ){
        if( car1.model < car2.model ){
            return true;
        }else if( car1.model == car2.model ){
            if( car1.year < car2.year ){
                return true;
            }
        }
    }

    return false;
}

Мой инстинктивный подход кажется громоздким, особенно для более чем 3 полей. Как бы вы структурировали эту серию сравнений в C ++? Другие языки предоставляют более сжатый или элегантный синтаксис?

Ответы [ 7 ]

5 голосов
/ 28 февраля 2009

Что ж, если ваша функция достигает возврата в предложении if , нет необходимости в явном else , поскольку она уже выручилась бы. Что может сэкономить на «отступной долине»:

bool carLessThanComparator( const Car & car1, const Car & car2 ) {
    if( car1.make < car2.make )
        return true;

    if ( car1.make != car2.make )
        return false;

    if( car1.model < car2.model )
        return true;

    if( car1.model != car2.model )
        return false;

    if( car1.year < car2.year )
        return true;

    return false;
}

Мне также нравится LISPish подход короткого замыкания MarkusQ.

4 голосов
/ 28 февраля 2009

Лично я бы предложил НЕ использовать операторы! = Или ==, как мы, кажется, здесь рекомендуем - для этого необходимо, чтобы аргументы / члены имели операторы как «меньше», так и «равно», просто чтобы выполнить проверку «меньше» для содержащего их класса - использование оператора меньше, чем нужно, и избавит вас от избыточности и потенциальных дефектов в будущем.

Предлагаю вам написать:

bool operator<(const Car &car1, const Car &car2) 
{
    if(car1.make < car2.make)
        return true;
    if(car2.make < car1.make)
        return false;

    if(car1.model < car2.model)
        return true;
    if(car2.model < car1.model)
        return false;

    return car1.year < car2.year;
}
4 голосов
/ 28 февраля 2009

Если это часто случается, вы можете поместить такой шаблон в общий заголовок:

template<typename T, typename A1, typename A2, typename A3>
bool
do_less_than(
        const typename T& t1,
        const typename T& t2,
        const typename A1 typename T::* a1,
        const typename A2 typename T::* a2,
        const typename A3 typename T::* a3)
{
    if ((t1.*a1) < (t2.*a1)) return true;
    if ((t1.*a1) != (t2.*a1)) return false;
    if ((t1.*a2) < (t2.*a2)) return true;
    if ((t1.*a2) != (t2.*a2)) return false;
    return (t1.*a3) < (t2.*a3);
}

При необходимости добавьте другие шаблоны для различного числа аргументов. Для каждой функции меньше, чем вы можете сделать что-то вроде этого:

bool carLessThanComparator(const Car& car1, const Car& car2)
{
    return do_less_than(car1, car2, &Car::make, &Car::model, &Car::year);
}
3 голосов
/ 31 марта 2013

Я знаю, что это старый вопрос, но для будущих посетителей: современное решение C ++ 11 заключается в использовании std :: tie

struct Car{
    Manufacturer make;
    ModelName model;
    Year year;
};

bool operator<(Car const& lhs, Car const& rhs)
{
    return std::tie(lhs.make, lhs.model, lhs.year) < std::tie(rhs.make, rhs.model, rhs.year);
}

std::tie преобразует структуру в std::tuple, так что вышеприведенный оператор сравнения делегирует std::tuple::operator<. Это, в свою очередь, делает лексикографическое сравнение с порядком, в котором члены распределены в std::tie.

Лексикографическое сравнение замкнуто так же, как и в других решениях этого вопроса. Но даже этого достаточно, чтобы определить на лету внутри лямбда-выражения C ++. Для классов с закрытыми членами-данными лучше всего определять внутри класса функцию friend.

2 голосов
/ 28 февраля 2009

Лично я бы переопределил ==, <,> и любые другие необходимые операторы. Это очистило бы код не в сравнении, а в том случае, когда вам нужно сделать сравнение. Для фактического сравнения я бы написал так же, как сказал Crashworks.

bool operator<(const Car &car1, const Car &car2) {
    if(car1.make < car2.make)
        return true;
    if(car1.make != car2.make)
        return false;
    if(car1.model < car2.model)
        return true;
    if(car1.model != car2.model)
        return false;
    return car1.year < car2.year;
}
2 голосов
/ 28 февраля 2009
bool carLessThanComparator( const Car & car1, const Car & car2 ){
    return (
      ( car1.make  < car2.make  ) or (( car1.make  == car2.make  ) and
      ( car1.model < car2.model ) or (( car1.model == car2.model ) and
      ( car1.year  < car2.year  ) 
      )));

- MarkusQ

1 голос
/ 04 февраля 2010

Мне было интересно то же самое, что и ОП, и наткнулся на этот вопрос. После прочтения ответов janm и RnR вдохновили меня написать шаблонную функцию lexicographicalMemberCompare, которая использует только operator< для сравниваемых членов. Он также использует boost :: tuple , так что вы можете указать столько членов, сколько захотите. Вот оно:

#include <iostream>
#include <string>
#include <boost/tuple/tuple.hpp>

template <class T, class Cons>
struct LessThan
{
    static bool compare(const T& lhs, const T& rhs, const Cons& cons)
    {
        typedef LessThan<T, typename Cons::tail_type> NextLessThan;
        typename Cons::head_type memberPtr = cons.get_head();
        return lhs.*memberPtr < rhs.*memberPtr ?
            true :
            (rhs.*memberPtr < lhs.*memberPtr  ?
                false :
                NextLessThan::compare(lhs, rhs, cons.get_tail()));
    }
};

template <class T>
struct LessThan<T, class boost::tuples::null_type>
{
    static bool compare(const T& lhs, const T& rhs,
                        const boost::tuples::null_type& cons)
    {
        return false;
    }
};

template <class T, class Tuple>
bool lexicographicalMemberCompare(const T& lhs, const T& rhs,
                                  const Tuple& tuple)
{
    return LessThan<T, typename Tuple::inherited>::compare(lhs, rhs, tuple);
}

struct Car
{
    std::string make;
    std::string model;
    int year;
};

bool carLessThanCompare(const Car& lhs, const Car& rhs)
{
    return lexicographicalMemberCompare(lhs, rhs,
        boost::tuples::make_tuple(&Car::make, &Car::model, &Car::year));
}

int main()
{
    Car car1 = {"Ford", "F150", 2009};
    Car car2 = {"Ford", "Escort", 2009};
    std::cout << carLessThanCompare(car1, car2) << std::endl;
    std::cout << carLessThanCompare(car2, car1) << std::endl;
    return 0;
}

Надеюсь, это кому-нибудь пригодится.

...