我正在定义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
的(可见)方法”。
答案 0 :(得分:6)
利用Octave的Enum
实例并使用Show
和Read
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
即。 fromEnum
和toEnum
在八度和整数之间进行转换,以便O1
< - > 0
和O2
< - > 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之类的东西来做,但这是一个糟糕的主意。
实际上,第一个坏主意可能依赖于show
。 Show
类传统上用于“序列化”数据,而Read
类将反序列化它们。如果你想打印输出,你最好编写自己的Octave -> String
函数。为此,您可以依赖show
的结果(并截断结果)。但是,最有效的解决方案可能是直接编码,如amalloy所建议的那样。