我会使用 isDigit
函数并保留你对于时间的定义。
import Data.Char (isDigit)
data Time = Time {hour :: Int,
minute :: Int
}
您在代码中使用了newTime
,但没有定义它,所以我自己写了一个来使我的代码编译通过!
newTime :: Int -> Int -> Time
newTime h m | between 0 23 h && between 0 59 m = Time h m
| otherwise = error "newTime: hours must be in range 0-23 and minutes 0-59"
where between low high val = low <= val && val <= high
首先,您的展示实例有点错误,因为
show $ Time 10 10
会得到
"010:010"
。
instance Show Time where
show (Time hour minute) = (if hour > 9 -- oops
then (show hour)
else ("0" ++ show hour))
++ ":" ++
(if minute > 9 -- oops
then (show minute)
else ("0" ++ show minute))
让我们来看一下
readsPrec
:
*Main> :i readsPrec
class Read a where
readsPrec :: Int -> ReadS a
...
*Main> :i ReadS
type ReadS a = String -> [(a, String)]
那是一个解析器 - 它应该返回未匹配的剩余字符串,而不仅仅是
""
,所以您是正确的,
""
是错误的:
*Main> read "03:22" :: Time
03:22
*Main> read "[23:34,23:12,03:22]" :: [Time]
*** Exception: Prelude.read: no parse
由于在第一次读取时您丢弃了,23:12,03:22]
,因此它无法解析。
让我们进行一些重构,随着读取的进行逐步处理输入:
instance Read Time where
readsPrec _ input =
let (hours,rest1) = span isDigit input
hour = read hours :: Int
(c:rest2) = rest1
(mins,rest3) = splitAt 2 rest2
minute = read mins :: Int
in
if c==':' && all isDigit mins && length mins == 2 then
[(newTime hour minute,rest3)]
else []
举个例子,
Main> read "[23:34,23:12,03:22]" :: [Time]
[23:34,23:12,03:22]
*Main> read "34:76" :: Time
*** Exception: Prelude.read: no parse
然而,它允许输入"3:45"并将其解释为"03:45"。我不确定这是否是一个好主意,因此也许我们可以添加另一个测试length hours == 2
。
如果我们按照这种方式处理,我会放弃所有的分割和分隔操作,因此也许我更喜欢:
instance Read Time where
readsPrec _ (h1:h2:':':m1:m2:therest) =
let hour = read [h1,h2] :: Int
minute = read [m1,m2] :: Int
in
if all isDigit [h1,h2,m1,m2] then
[(newTime hour minute,therest)]
else []
readsPrec _ _ = []
我认为这实际上更加清晰简洁。
这次,它不允许 "3:45"
:
*Main> read "3:40" :: Time
*** Exception: Prelude.read: no parse
*Main> read "03:40" :: Time
03:40
*Main> read "[03:40,02:10]" :: [Time]
[03:40,02:10]
Int
之前,我仍然需要检查是否有数字。 - AndrewC9:3
,所以我进行了编辑。原因是splitAt 2 "3"
给出的结果是("3","")
,因此在数据不足的情况下会悄悄地成功,而如果它在列表中,例如splitAt 2 "3]"
给出的结果是("3]","")
,并且"3]"
无法被读取。 - AndrewC