2010-10-05 2 views
8

Написание оператора <() для структуры кажется более четким, чем запись классического сравнения треугольников.Как вы пишете оператор() или менее чем функтор, чем функция сравнения с треугольником

, например, сортировать следующие

struct S { 
    int val; 
}; 

вы можете написать оператор <()

bool operator< (const S &l, const S &r) { 
    return l.val < r.val; 
} 

или, в trivalue функцию (обычно следующим образом)

int compare(const S &l, const S &r) { 
    if(r.val > l.val) return 1; 
    if(r.val < l.val) return -1; 
    return 0; 
} 

Первый более четкий, поэтому вы можете сказать, что качество кода лучше. Последнее заставляет вас думать о 3 случаях, что усложняет код.

Но эта мысль немного обманывают в более сложные структуры:

struct S { 
    int x; 
    int y; 
}; 

следующее ясно, и begginners склонны писать вот так

bool operator< (const S &l, const S &r) { 
    if(l.x < r.x) return true; 
    if(l.y < r.y) return true; 
    return false; 
} 

но это неправильно! Вы не можете правильно сортировать с этим!

И это занимает некоторое время, чтобы подумать, что вы на самом деле должны написать это как так

bool operator< (const S &l, const S &r) { 
    if(l.x < r.x) return true; 
    if(l.x > r.x) return false; 
    if(l.y < r.y) return true; 
    if(l.y > r.y) return false; 
    return false; 
} 

для того, чтобы работать правильно.

Можете ли вы и у вас написать эту функцию сравнения в удобном и понятном образе? Старая функция сравнения треугольников, по крайней мере, «вынуждающая» вас думать о>, < и == случаях.

+0

Я не вижу разницы между двумя последними операциями <функции. – Donotalo

+2

@ Donotalo: Тогда будьте внимательнее. –

+0

получил. уф! .. – Donotalo

ответ

3

Дело в том, что вы хорошо с только декларирует одну trivalue функцию сравнения, если осуществлять автоматическую генерацию всех операторов с помощью: http://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick

+0

+1, очень напоминает Haskell. Это было бы также моим советом. –

+0

Вам не нужна функция сравнения треугольников для генерации всех операторов, она просто требует объединения двух сравнений. –

+0

@Mark Вам нужно три значения для генерации '<,>, <=,> =, ==,! =' –

4

Я хотел бы сделать это следующим образом:

bool operator< (const S &l, const S &r) 
{ 
    if(l.x != r.x) return l.x < r.x; 
    else return l.y < r.y; 
} 

EDIT: обратите внимание, что это также является одной из полезных функций std::pair - она ​​уже определяет это, поэтому вы не можете ошибиться.

4

В int случае вы можете просто написать:

return l.x < r.x || (l.x == r.x && l.y < r.y); 

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

return l.x < r.x || (!(r.x < l.x) && l.y < r.y); 

Расширение для большего числа участников:

Если вы можете организовать ваши члены, чтобы быть в массиве или другой контейнер, вы можете использовать std::lexicographical_compare.

+0

Хмм, мне нравится идея lexicographic_compare !! –

0

Одна вещь, которую я сделал однажды, которая казалась полезной идиомой, заключалась в том, чтобы написать функцию сравнения, которая возвращает логическое заданное, принимает два аргумента плюс логическое выражение «bias». Функция возвращает true, если первый аргумент больше, или если задано «смещение» и два аргумента равны. Таким образом, сравнение, результат которого зависит от двух суб-сравнений будет:

 
    if (compare(a.part1,b.part1,compare(a.part2,b.part2,bias))) 
    ... 

Обратите внимание, что это будет делать «лишние» сравнения, так как part2 будет сравниваться-х, даже если part1 сравнивать было бы достаточно, чтобы дать ответ, но если избегать избыточных тестов на равенство.

В противном случае, используя логику три-значение, можно было бы сделать что-то вроде:

 
    int result; 

    if ((result = compare(a.part1,b.part1)) != 0) return result; 
    if ((result = compare(a.part2,b.part2)) != 0) return result; 

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

5

Если я не забочусь о производительности или компилятор блевать, я предпочитаю использовать это:

return make_tuple(l.x, l.y, ...) < make_tuple(r.x, r.y, ...); 

И чуть менее дорогим в плане версии копий:

return tie(cref(l.x), cref(l.y), ...) < tie(cref(r.x), cref(r.y), ...); 

Кстати, вторая версия также работает с lvalues.

+2

Интересная идея. Но не будет 'return tie (l.x, l.y, ...) UncleBens

+0

@UncleBens ... Святое дерьмо, я об этом не думал! Я предполагаю, что мой первоначальный отказ от ответственности был еще хуже, чем я думал :) Хотя это не работает, если l или r являются или содержат значения const. – MSN

+0

Кажется компиляцией для меня (GCC 4.3): он создает ссылки на const. – UncleBens

0

Для первых три-значенийcompare() функции

int compare(const S &l, const S &r) { 
    return r.val - l.val; 
} 

Для последующего

bool operator< (const S &l, const S &r) { 
    return (l.x < r.x) || ((l.x == r.x) && (l.y < r.y)); 
} 
+1

Трехзначная версия не может использоваться в общем случае, потому что результат может быть недостаточным или переполненным. – UncleBens

1

Это не понятнее или короче, чем последний пример, но у него есть преимущество не требуя ничего, кроме operator< на членах.

bool operator< (const S &l, const S &r) { 
    if(l.x < r.x) return true; 
    if(r.x < l.x) return false; 
    if(l.y < r.y) return true; 
    if(r.y < l.y) return false; 
    return false; 
} 

Последний случай всегда может быть упрощен, к сожалению, предыдущие случаи всегда должны быть более длинными.

bool operator< (const S &l, const S &r) { 
    if(l.x < r.x) return true; 
    if(r.x < l.x) return false; 
    return l.y < r.y; 
}