4

我正在定义一个 Octave 类型:

data Octave = 1 | 2 | 3
  deriving (Show, Read, Ord, Enum)

由于“1”对数据构造函数标识符无效,我必须这样做:

data Octave = O1 | O2 | O3
  deriving (Show, Eq, Read, Ord, Enum)

现在,如果我show Octave O1显示“O1”,这不是我想要的。我希望结果为“1”。我知道我们可以像这样自定义我们的 Show 行为:

instance Show Blabla where                                                                                       
  show (Blabla ints chars list num) =                                                                            
    "integers = " ++ show ints ++ "\n"

但问题是我使用的是枚举类型,这意味着它除了标识符名称“O1”之外没有任何值。如何在 Haskell 中访问它?

另一个问题:我怎样才能读回来?

read "O1" :: Octave有效,但我想要read "1" :: Octave

instance Read Octave where
  read "1" = O1
  read "2" = O2
  read "3" = O3

这不起作用:“read不是类的(可见)方法Read”。

4

5 回答 5

6

利用 Octave 的Enum实例并使用ShowandRead实例,Int我们可以像这样实现显示和读取:

data Octave = O1 | O2 | O3 deriving (Eq, Ord, Enum)

instance Show Octave where
    show o = show (fromEnum o + 1)

instance Read Octave where
    readsPrec prec = map (\(n,s) -> (toEnum (n - 1), s)) . readsPrec prec

fromEnumtoEnum在八度和整数之间进行转换,以便O1<->0O2<-> 1,所以我们必须在阅读和写作中调整一个。

于 2013-09-04T08:37:02.450 回答
5

这是一种不同的方法:

data Octave_ = O1 | O2 | O3 deriving (Show, Eq, Read, Ord, Enum)
newtype Octave = O { unO :: Octave_ }  deriving (Eq, Ord, Enum)

instance Show Octave where
    show = tail . show . unO

这取决于你在做什么,这可能是好是坏。

于 2013-09-04T09:39:22.787 回答
4

看起来你只需要这个,对吧?

instance Show Octave where
  show O1 = "1"
  show O2 = "2"
  show O3 = "3"

用三个子句定义show,让模式匹配器找出来。

于 2013-09-04T06:33:01.873 回答
2

您始终可以将现有实例用于 Int,如下所示:

data Octave = O1 | O2 | O3 deriving (Enum,Bounded)

instance Show Octave where
   show = show . (+1) . fromEnum

instance Read Octave where
   readsPrec pr = map (\ (int,str) -> ((toEnum (int-1)),str) . readsPrec pr

这正确地将 O1..O3 渲染为 1..3 并将它们读回。唯一的陷阱是在尝试读取不同的整数时,例如 4:

*** Exception: toEnum{Octave}: tag (3) is outside of enumerations'range (0,2)

这可以通过编写更多代码并检查 readsPred 中的有效值来解决。

于 2013-09-04T07:17:46.370 回答
1

看起来您想要访问标识符名称。您可以使用模板 haskell之类的东西来做到这一点,但这是一个糟糕的主意。

其实,拳头坏主意大概就是靠showShowclass 传统上用于“序列化”数据,而Readclass 将反序列化它们。如果你想漂亮地打印输出,你最好编写自己的Octave -> String函数。为此,您可以依靠show(并截断结果)的结果。然而,最有效的解决方案可能是直接对其进行编码,正如amalloy所建议的那样。

于 2013-09-04T06:45:03.427 回答