有什么方法可以在Haskell中定义Enum来包装? [英] Is there some way to define an Enum in haskell that wraps around?

查看:90
本文介绍了有什么方法可以在Haskell中定义Enum来包装?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我正在设计一个大富翁游戏:

  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

(受到 http://www.mail-archive.com/haskell-cafe@haskell.org/msg37258。如果你真的需要使用 succ pred 相反,我不相信有关 Enum 的实现的法律,例如 succ(succ x)/ =所有 x (尽管这是大多数人的工作原理)。因此,你可以为你的类型写一个 Enum 的自定义实现,以展现你想要的环绕:

 实例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/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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆