问题描述
假设我正在设计一个大富翁游戏: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 ,因为这违反了关于有界 succ maxBound 应该会导致运行时错误。
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/[email protected]/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来包装?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!