循环一个值(Enum a,Bounded a)=>一个

时间:2015-10-07 20:14:25

标签: haskell enums

我正在寻找这对功能

previous :: (Enum a, Bounded a) => a -> a
next     :: (Enum a, Bounded a) => a -> a

如果结果值受边界限制,则previouspred :: Enum a => a -> a,否则将其循环到另一边(nextsucc对称)

实施例

data X = A | B | C deriving (Bounded, Enum)
next A = B
next B = C
next C = A

如果我要添加Eq a => a约束,这将很容易,如similar questions中所述。 但这种约束似乎是不必要的,因为Bounded a => a类型对我来说听起来应该能够判断它是否在界限内。 succpred函数本身对此有一定的控制权,但我宁愿不在我的纯代码中使用异常:

Prelude> succ (maxBound :: Int)
*** Exception: Prelude.Enum.succ{Int}: tried to take `succ' of maxBound

succ / pred如何在不需要Eq a => a(甚至Bounded a => a)的情况下对边界进行测试?我可以在自己的函数中复制这种行为吗?或者是否有另一种方法来编写具有此行为的函数,而不需要Eq约束?

3 个答案:

答案 0 :(得分:5)

Enum类定义了两个函数

  toEnum :: Enum a => Int -> a
  fromEnum :: Enum a => a -> Int

如果Enum的范围符合Enum的大小,则可以使用这些函数将Int与另一个函数进行比较。如果您愿意接受这种轻微限制,可以按如下方式编写函数。

{-# LANGUAGE ScopedTypeVariables #-}

previous :: forall a . (Enum a, Bounded a) => a -> a
previous x | from x > from minBound = pred x 
           | otherwise              = maxBound where from :: a -> Int; from = fromEnum 

next :: forall a . (Enum a, Bounded a) => a -> a
next x | from x < from maxBound = succ x 
       | otherwise              = minBound where from :: a -> Int; from = fromEnum  

这些功能有几个假设;给定(Enum a, Bounded a)时,a的每个值都介于minBoundmaxBound之间,包括{to/from}Enum保留此排序,与排序有关整数。这对于简单的枚举是正确的:

>import Control.Arrow
>map (next &&& previous) [A,B,C]
[(B,C),(C,A),(A,B)]

答案 1 :(得分:4)

有什么问题?如果您也可以添加Eq课程,那很简单:

module CycleEnum where

data XEnum = A1 | A2 | A3 deriving (Bounded, Enum, Eq, Show)

cyclePrev :: (Eq a, Enum a, Bounded a) => a -> a
cyclePrev x = if x == minBound then maxBound else pred x

cycleNext :: (Eq a, Enum a, Bounded a) => a -> a
cycleNext x = if x == maxBound then minBound else succ x

没有Eq,它会稍微复杂一些......

module CycleEnum where

data XEnum = A1 | A2 | A3 deriving (Bounded, Enum, Show)

isSingle :: [a] -> Bool
isSingle [x] = True
isSingle _ = False

isLastElement :: Enum a => a -> Bool
isLastElement x = isSingle $ enumFrom x

isFirstElement :: (Enum a, Bounded a) => a -> Bool
isFirstElement x = isSingle $ enumFromTo minBound x

cyclePrev :: (Enum a, Bounded a) => a -> a
cyclePrev x = if isFirstElement x then maxBound else pred x

cycleNext :: (Enum a, Bounded a) => a -> a
cycleNext x = if isLastElement x then minBound else succ x

请注意,我使用模式匹配编写了isSingle,而不是通过调用length;因为如果你只想知道列表中是否包含一个元素,那么计算列表的长度可能效率非常低。

答案 2 :(得分:2)

看起来,对于某些类型类实例,它只是像您对示例所做的那样定义函数:

instance Enum Ordering where
  succ LT = EQ
  succ EQ = GT
  succ GT = error "Prelude.Enum.Ordering.succ: bad argument"

  pred GT = EQ
  pred EQ = LT
  pred LT = error "Prelude.Enum.Ordering.pred: bad argument"

否则,正如你所说,你似乎很可能需要允许Eq,因为从根本上说必须有一些比较的概念(为了比较当前值的界限) ,如果不能轻易列举该类型的完整空间。