No es infrecuente que se quiera implementar el operador <=>
(comparación o "nave espacial") en un tipo de datos de producto, es decir, una clase con múltiples campos (¡esperamos!) tiene <=>
implementado), comparando los campos en un cierto orden.Impulso de Ruby <=> Combinator
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
Esto es tedioso y propenso a errores, especialmente con muchos campos. Es lo suficientemente propenso a errores que a menudo siento que debería probar esa función en una unidad, lo que simplemente aumenta la tediosidad y la verbosidad.
Haskell ofrece una particular buena manera de hacer esto:
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]
(Para aquellos que no están familiarizados con fold
, lo anterior se expande a
comparing f1 `mappend` comparing f2 `mappend` comparing f3
que produce una función que puede ser aplicado a dos D
s, para producir una Ordering
.)
El Defintion de compareD
es tan simple que obviamente es correcto, y no sentiría la necesidad de probarlo unitariamente incluso sin verificación de tipo estático.
En realidad, la pregunta puede ser incluso un poco más interesante que esto, ya que puede que no desee utilizar sólo el <=>
operador estándar, pero una especie de diferentes maneras en diferentes momentos, por ejemplo:
sortByOrderings :: [a -> a -> Ordering] -> [a] -> [a] sortByOrderings = sortBy . foldl1 mappend sortByF3F1 = sortByOrderings [comparing f3, comparing f1] sortByF2F3 = sortByOrderings [comparing f2, comparing f3]
Por lo tanto, las preguntas:
- ¿Cuál es la forma típica de implementar este tipo de cosas en Ruby?
- ¿Cuál es la mejor forma de hacerlo utilizando solo lo que se define en las bibliotecas estándar?
- ¿Cuán cerca se puede llegar al código Haskell anterior, y qué tan confiable es, en comparación? Si es necesario, ¿cómo se puede asegurar que los campos tengan implementados correctamente los operadores
<=>
o<
y>
?
Incidentalmente, si bien esta es una pregunta de Ruby, estoy contento de considerar la discusión de las técnicas de Haskell sobre el tema si los mayores de este sitio lo aceptan. Por favor, siéntase libre de comentar si es apropiado o no y, si lo es, etiquete también esta publicación como 'haskell'.
Cosas geniales. No me da la misma cosa fácil de 'ordenar por' que tiene Haskell, ¡pero sin duda hace un gran trabajo al lidiar con las comparaciones predeterminadas! –
Tenga en cuenta que puede usar el tipo de enumeración Enumerable casi de la misma manera que Haskell's List.sortBy (solo sin currying, lo siento). Y Enumerable # sort_by le permite definir la clave de clasificación en el tiempo de ordenamiento. – rampion
+1, uno de los mejores pedacitos de código de Ruby que he visto en SO – Allyn