Нередко хочется реализовать оператор <=>
(сравнение или "космический корабль") для типа данных продукта, т. Е. Класса с несколькими полями (все из которых (мы надеемся!) Уже имеют <=>
реализовано), сравнивая поля в определенном порядке.
def <=>(o)
f1 < o.f1 && (return -1)
f1 > o.f1 && (return 1)
f2 < o.f2 && (return -1)
f2 > o.f2 && (return 1)
return 0
end
Это утомительно и подвержено ошибкам, особенно с большим количеством полей. Он достаточно подвержен ошибкам, поэтому я часто чувствую, что должен выполнить модульное тестирование этой функции, что только добавляет утомительности и многословия.
Haskell предлагает особенно хороший способ сделать это:
import Data.Monoid (mappend)
import Data.Ord (comparing)
-- From the standard library:
-- data Ordering = LT | EQ | GT
data D = D { f3 :: Int, f2 :: Double, f1 :: Char } deriving Show
compareD :: D -> D -> Ordering
compareD = foldl1 mappend [comparing f1, comparing f2, comparing f3]
(Для тех, кто не знаком с fold
, вышеизложенное расширяется до
comparing f1 `mappend` comparing f2 `mappend` comparing f3
, которая производит функцию, которая может быть применена к двум D
с, чтобы создать Ordering
.)
Определение compareD
настолько просто, что оно, очевидно, правильно, и я не чувствую необходимости его модульного тестирования даже без статической проверки типов.
На самом деле, вопрос может быть даже немного более интересным, чем этот, так как я, возможно, не захочу использовать только стандартный оператор <=>
, но сортирую по-разному в разное время, например:
sortByOrderings :: [a -> a -> Ordering] -> [a] -> [a]
sortByOrderings = sortBy . foldl1 mappend
sortByF3F1 = sortByOrderings [comparing f3, comparing f1]
sortByF2F3 = sortByOrderings [comparing f2, comparing f3]
Итак, вопросы:
- Каков типичный способ реализации такого рода вещей в Ruby?
- Какой самый лучший способ сделать это, используя только то, что определено в стандартных библиотеках?
- Насколько можно приблизиться к приведенному выше коду Haskell и насколько он надежен по сравнению? Если необходимо, как можно убедиться, что поля имеют правильно реализованные операторы
<=>
или <
и >
?
Между прочим, хотя это вопрос Ruby, я с удовольствием рассмотрю обсуждение техник Haskell по теме, если с этим согласны старейшины этого сайта. Пожалуйста, не стесняйтесь комментировать, является ли это уместным или нет, и, если это так, пометьте также этот пост 'haskell'.