给定数据类型,例如
data Foo = Bar | Baz | Qux
我希望这种类型有多种不同的排序,以下是实现这一目标的最常见/标准方式吗?
newtype FooPriorityA = FooPriorityA { unFooPriorityA :: Foo }
instance Ord FooPriorityA where
compare (FooPriorityA x) (FooPriorityA y) = f x `compare` f y
where f :: Foo -> Int
f Baz = 1
f Bar = 2
f Qux = 3
newtype FooPriorityB = FooPriorityB ... and so on
委托Int的Ord实例那样疯狂吗?与写compare
的n ^ 2比较相比,感觉更安全,工作量更少。
我可能忽略了任何明显的缺陷" hack"?它甚至是一个" hack"?
答案 0 :(得分:7)
这绝对合理。事实上,您可以使用the comparing
function from Data.Ord
使其更加直接:
instance Ord FooPriorityA where
compare (FooPriorityA x) (FooPriorityA y) = comparing f x y
where f :: Foo -> Int
f Baz = 1
f Bar = 2
f Qux = 3
虽然取决于您的偏好,compare
的版本可能更符合您的喜好。
答案 1 :(得分:5)
只要您指定案例,您只需要O( n )(2 n - 1,确切地说)定义来指定总订单增加优先顺序:
instance Ord FooPriorityA where
compare (FooPriorityA x) (FooPriorityA y) | x == y = EQ -- Use the Eq instance
-- Bar is the smallest
| x == Bar = LT
| y == Bar = GT
-- Baz is the next smallest
| x == Baz = LT
| y == Baz = GT
-- Proceed in the desired order.
-- You don't need to say anything
-- explicit about the largest value
第一种情况(x == y
)涵盖O(n)种可能性。下面的每个案例可能看起来都不完整,但它带有隐含的信息,即每个前面的案例都是错误的。例如,x == Bar = LT
并不意味着{em> x == Bar
评估为LT
的每个案例;已处理x == Bar && y == Bar
的情况,因此第二种情况实际上是隐式x == Bar && y /= Bar
。
同样,案例4(x == Baz
)假设y /= Baz
(由案例1未能匹配)和y /= Bar
(假设案例3未能匹配) )。因此,y
的任何剩余可能值实际上都大于Baz
。
你去的列表越往下,剩下的未处理案例就越少。到最后,你不需要对最大的项目做任何明确的说明;关于它与其他 n 的比较的所有信息 - 前面的案例已经捕获了1个项目。
另外,请记住,f
的定义是fromEnum
类实例的最小实现的一半(Enum
),然后您可以使用它来定义你的Ord
个实例。
instance Enum FooPriorityA where
fromEnum Bar = 1
fromEnum Baz = 2
fromEnum Qux = 3
toEnum 1 = Bar
toEnum 2 = Baz
toEnum 3 = Qux
instance Ord FooPriorityA where
compare x y = compare (fromEnum x) (fromEnum y)
-- compare = compare `on` Data.Function.fromEnum
答案 2 :(得分:0)
如何使函数根据表示所需顺序的列表生成从Foo
到Int
的映射:
import Data.List
data Foo = Bar | Baz | Qux deriving Eq
newtype FooPriorityA = FooPriorityA { unFooPriorityA :: Foo } deriving Eq
fooOrdering :: [Foo] -> Foo -> Foo -> Ordering
fooOrdering orderedList a b = f a `compare` f b
where f x = elemIndex x orderedList
instance Ord FooPriorityA where
compare (FooPriorityA x) (FooPriorityA y) = fooOrdering [Baz, Bar, Qux] x y
您甚至可以添加一个断言,以确保所有元素在列表中只出现一次。