有什么方法可以在Haskell中定义Enum来包装? [英] Is there some way to define an Enum in haskell that wraps around?
问题描述
data board = GO | A1 | CC1 | A2 | T1 | R1 | B1 | CH1 | B2 | B3 |
JAIL | C1 | U1 | C2 | C3 | R2 | D1 | CC2 | D2 | D3 |
FP | E1 | CH2 | E2 | E3 | R3 | F1 | F2 | U2 | F3 |
G2J | G1 | G2 | CC3 | G3 | R4 | CH3 | H1 | T2 | H2
deriving(Show,Enum,Eq)
我想要:
succ H2 == GO
但是相反:
***例外:succ {Board}:尝试将最后一个标记的'succ'枚举
是否存在用于表示枚举的类型类型?
最简单的选择是让Board成为 Bounded
的实例(也可以是自动派生的),并使用以下帮助函数:
next ::(Enum a,Bounded a)=> a - > a
next = turn 1
prev ::(Enum a,Bounded a)=> a - > a
prev = turn(-1)
turn ::(Enum a,Bounded a)=> Int - > a - > (fromEnum e)n)
其中
add mod xy =(x + y + mod) rem` mod
示例使用:
> next H2
G0
> prev G0
H2
>下一个F1
F2
实例Enum Board其中
到Enum 0 = G0
到Enum 1 = A1
...
到Enum 40 = H2
到Enum x = toEnum (x`mod` 40)
fromEnum G0 = 0
fromEnum A1 = 1
...
fromEnum H2 = 40
尽管实现起来非常繁琐。另外,当使用循环定义 Enum
时,该类型不应该实现 Bounded
,因为这违反了关于有界
Consider I was designing a Monopoly game:
data Board = GO | A1 | CC1 | A2 | T1 | R1 | B1 | CH1 | B2 | B3 |
JAIL | C1 | U1 | C2 | C3 | R2 | D1 | CC2 | D2 | D3 |
FP | E1 | CH2 | E2 | E3 | R3 | F1 | F2 | U2 | F3 |
G2J | G1 | G2 | CC3 | G3 | R4 | CH3 | H1 | T2 | H2
deriving (Show, Enum, Eq)
I want:
succ H2 == GO
But instead:
*** Exception: succ{Board}: tried to take `succ' of last tag in enumeration
Is there a typeclass for expressing an enumeration that wraps around?
The simplest option is to make Board an instance of Bounded
(can be auto derived as well), and use the following helper functions:
next :: (Enum a, Bounded a) => a -> a
next = turn 1
prev :: (Enum a, Bounded a) => a -> a
prev = turn (-1)
turn :: (Enum a, Bounded a) => Int -> a -> a
turn n e = toEnum (add (fromEnum (maxBound `asTypeOf` e) + 1) (fromEnum e) n)
where
add mod x y = (x + y + mod) `rem` mod
Example Use:
> next H2
G0
> prev G0
H2
> next F1
F2
(inspired by the the thread at http://www.mail-archive.com/haskell-cafe@haskell.org/msg37258.html ).
If you really need to use succ
and pred
instead, I don't believe there is any laws regarding implementations of Enum
such that succ (succ x) /= x
for all x
(even though that is how most work). Therefore you could just write a custom implementation of Enum
for your type that exhibits the wraparound you desire:
instance Enum Board where
toEnum 0 = G0
toEnum 1 = A1
...
toEnum 40 = H2
toEnum x = toEnum (x `mod` 40)
fromEnum G0 = 0
fromEnum A1 = 1
...
fromEnum H2 = 40
That is very tedious to implement though. Also, the type shouldn't also implement Bounded
when using a circular definition of Enum
, as that breaks a rule regarding Bounded
that succ maxBound
should result in a runtime error.
这篇关于有什么方法可以在Haskell中定义Enum来包装?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!