我想实现一个read实例,它允许我读取一个字符串(例如:“ - - 8 - 3 - ”)并构造一个包含这些值的列表。
data Value a = Nul | Val a
showsValue :: (Show a) => Value a -> ShowS
showsValue (Val x) = ("Value" ++) . shows x
showsValue (Nul) = ("Nothing 0" ++)
instance Show a => Show (Value a) where
showsPrec _ x = showsValue x
instance Read a => Read (Value a) where
readsPrec _ m = readsMatrix m
readsMatrix :: (Read a) => ReadS (Value a)
readsMatrix ('-':s) = [(Nul, rest) | (' ',rest) <- reads s]
readsMatrix s = [(Val x,rest)| (x,' ':rest) <- reads s]
执行此测试后:
read "- 8 - - 3" :: Value Int
我收到错误*** Exception: Prelude.read: no parse
我在这里做错了什么?
更新
readsMatrix ('-':s) = [(Nul, dropWhile isSpace s)]
readsMatrix s = [(Val x, dropWhile isSpace rest) | (x,rest) <- reads s]
答案 0 :(得分:2)
在readsMatrix
功能中,您有
readsMatrix ('-':s) = [(Nul, rest) | (' ',rest) <- reads s]
这意味着在消耗'-'
之后,输入字符串的其余部分将传递给
reads :: ReadS Char
但Read
的{{1}}实例需要用单引号括起来的字符,例如Char
。
要在结果中获取元素'h'
,您需要 - 在它之前的可选空格之后 - 字符序列(' ',rest)
。您没有预期输入的那些。你想要做的是删除'\'' : ' ' : '\'' : whatever
之后的空格,所以如果空格是可选的,并且可能有多个字符长,
'-'
可以工作(如果还要忽略换行符,制表符等)。如果您只想要一个必填空间,则模式应为
readsMatrix ('-':s) = [(Nul, dropWhile isSpace s)]
第二个等式
readsMatrix ('-':' ':s) = [(Nul, s)]
可以按原样工作,但要求值后跟一个空格,例如readsMatrix s = [(Val x,rest)| (x,' ':rest) <- reads s]
将返回一个空列表。
我认为你宁可在那里选择空间,所以也许
readMatrix "3" :: [(Value Int, [Char])]
是你想要的,但由于readsMatrix s = [(Val x, dropWhile isSpace rest) | (x,rest) <- reads s]
忽略了大多数类型的前导空格,
reads
可能会更好。
但是你并没有在readsMatrix s = [(Val x, rest) | (x, rest) <- reads s]
之前忽略前导空格,所以也需要这样做。
使用
可能会更好地完成实施'-'
来自lex :: ReadS String
的,它大致按照Haskell语法将字符串拆分为标记,例如
Prelude
将左括号ghci> lex "(12 + 34) abc"
[("(","12 + 34) abc")]
分开,等分开左括号。
使用"12"
的一种可能性是,正确地忽略前导空格
lex
最后,
readsMatrix s = do
(tok, rest) <- lex s
case tok of
"-" -> return (Nul, rest)
_ -> case reads tok of
[(x,"")] -> return (Val x, rest)
_ -> []
即使这样,也会导致read "- 8 - - 3" :: Value Int
,因为*** Exception: Prelude.read: no parse
只有在转换后的令牌后面只有空格才会成功,但在转换初始read
后,仍有四个可转换令牌在输入的其余部分。
'-'
另一方面,应该成功。
答案 1 :(得分:2)
首先,不要费心去除空格,reads
:
readsMatrix :: (Read a) => ReadS (Value a)
readsMatrix ('-':s) = [(Nul, dropWhile (==' ') s)]
readsMatrix s = [(Val x,rest)| (x,rest) <- reads s]
您的读取实例现在适用于单个值:
*Main> read "4" :: Value Int
Value4
*Main> read "-" :: Value Int
Nothing 0
但是你想要读取由空格分隔的列表,所以由于这是非标准行为,你需要编写一个自定义readList :: :: ReadS [Value a]
instance Read a => Read (Value a) where
readsPrec _ m = readsMatrix m
readList s = [(map read.words $ s,"")]
所以现在
*Main> read "- 4 2 - 5" :: [Value Int]
[Nothing 0,Value4,Value2,Nothing 0,Value5]
但不幸的是,
*Main> read "- 4 2 \n- 5 4" :: [Value Int]
[Nothing 0,Value4,Value2,Nothing 0,Value5,Value4]
甚至更糟,
*Main> read "- 4 2 \n- 5 4" :: [[Value Int]]
*** Exception: Prelude.read: no parse
我可以看到,没有直截了当的方法,因为readListOfLists
类中没有Read
,所以为什么不创建一个独立的函数
matrix :: Read a => String -> [[Value a]]
matrix = map read.lines
这样
*Main> matrix "3 4 -\n3 - 6\n4 5 -" :: [[Value Int]]
[[Value3,Value4,Nothing 0],[Value3,Nothing 0,Value6],[Value4,Value5,Nothing 0]]
我认为Show
Value
的{{1}}实例有点误导(Nul
没有零,Val
未写Value
})。写下
data Value a = Nul | Val a deriving Show
以使其看起来或定义它以匹配Read
实例
instance Show a => Show (Value a) where
show Nul = "-"
show (Val a) = show a