如何在Haskell中编写自定义的show函数

时间:2013-09-04 06:27:26

标签: haskell enums

我正在定义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的(可见)方法”。

5 个答案:

答案 0 :(得分:6)

利用Octave的Enum实例并使用ShowRead 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,所以我们必须在阅读和写作中按一个调整。

答案 1 :(得分: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

根据你的行为,这可能是好事还是坏事。

答案 2 :(得分:4)

好像你需要的就是这个,对吧?

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

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

答案 3 :(得分: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中的有效值来解决。

答案 4 :(得分:1)

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

实际上,第一个坏主意可能依赖于showShow类传统上用于“序列化”数据,而Read类将反序列化它们。如果你想打印输出,你最好编写自己的Octave -> String函数。为此,您可以依赖show的结果(并截断结果)。但是,最有效的解决方案可能是直接编码,如amalloy所建议的那样。