有没有办法在包围的haskell中定义枚举?

Ara*_*ian 21 haskell

考虑一下我在设计一款大富翁游戏:

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)
Run Code Online (Sandbox Code Playgroud)

我想要:

succ H2 == GO
Run Code Online (Sandbox Code Playgroud)

但反而:

*** Exception: succ{Board}: tried to take `succ' of last tag in enumeration
Run Code Online (Sandbox Code Playgroud)

是否存在用于表达包含枚举的类型类?

Nei*_*own 29

比nanothief更简单的解决方案:

nextBoard :: Board -> Board
nextBoard H2 = GO
nextBoard t = succ t
Run Code Online (Sandbox Code Playgroud)

我认为你不能直接使用Enum来获得你想要的东西,但是这个解决方案会快速包装它以形成你想要的行为.


Dav*_*ani 22

最简单的选择是使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 -> a
turn n e = toEnum (add (fromEnum (maxBound `asTypeOf` e) + 1) (fromEnum e) n)
    where
      add mod x y = (x + y + mod) `rem` mod
Run Code Online (Sandbox Code Playgroud)

示例使用:

> next H2
G0
> prev G0
H2
> next F1
F2
Run Code Online (Sandbox Code Playgroud)

(受http://www.mail-archive.com/haskell-cafe@haskell.org/msg37258.html主题的启发).

如果你确实需要使用succ,并pred代替,我不相信有关于实施任何法律Enum,以便succ (succ x) /= x为所有x(即使那是多么最多的工作).因此,您可以Enum为您的类型编写一个自定义实现,以展示您希望的环绕:

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
Run Code Online (Sandbox Code Playgroud)

尽管如此,实施起来非常繁琐.此外,Bounded在使用循环定义时,类型也不应该实现Enum,因为这会破坏与此相关的规则Boundedsucc maxBound导致运行时错误.

  • 我建议不要制作succ maxBound = minBound; 有界类型类明确表示succ maxBound应该导致运行时错误. (6认同)

小智 7

我知道这是一个古老的问题,但是我只是遇到了这个问题,所以就这样解决了。

data SomeEnum = E0 | E1 | E2 | E3
               deriving (Enum, Bounded, Eq)

-- | a `succ` that wraps 
succB :: (Bounded a, Enum a, Eq a) => a -> a 
succB en | en == maxBound = minBound
         | otherwise = succ en

-- | a `pred` that wraps
predB :: (Bounded a, Enum a, Eq a) => a -> a
predB en | en == minBound = maxBound
         | otherwise = pred en  
Run Code Online (Sandbox Code Playgroud)

该解决方案既导出EnumBounded,但避免了滥用predsucc的建议。

偶然地,我发现

allSomeEnum = [minBound..maxBound] :: [SomeEnum] 
Run Code Online (Sandbox Code Playgroud)

可能会有用。那需要Bounded