3

我想实现一个 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]
4

2 回答 2

2

在您的readsMatrix功能中,您有

readsMatrix ('-':s) = [(Nul, rest) | (' ',rest) <- reads s]

这意味着在'-'消耗完之后,输入字符串的其余部分将传递给

reads :: ReadS Char

但是 for 的Read实例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]

可能更好。

但是您不会忽略 a 之前的前导空格'-',因此也需要这样做。

也许使用会更好地完成实施

lex :: ReadS String

Prelude, 大致按照 Haskell 语法将字符串拆分为标记,例如

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只有在转换后的标记后面只有空格时才会成功,但是在转换初始值之后'-',输入的其余部分中还有四个可转换的标记。

read "- 8 - - 3" :: [Value Int]

另一方面应该成功。

于 2012-11-25T19:01:44.930 回答
2

更正读取值 a

首先,不要费心删除空格,这一切都可以通过以下方式处理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

更正 [Value a] 的读数

但是您想读取以空格分隔的列表,因此由于这是非标准行为,您需要编写自定义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

读取矩阵

我可以看到没有直接的解决方法,因为类中没有readListOfListsRead所以为什么不制作一个独立的函数

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有点误导(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
于 2012-11-25T20:00:24.347 回答