3

文字列 (例: "- - 8 - 3 -") を読み取り、それらの値を含むリストを作成できる read のインスタンスを実装したいと考えています。

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

値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

[値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

マトリックスの読み取り

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は少し誤解を招くと思います(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 に答える
2

あなたのreadsMatrix関数では、あなたは

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

つまり、'-'が消費された後、入力文字列の残りの部分がに渡されます

reads :: ReadS Char

ただし、のReadインスタンスCharは、のように一重引用符で囲まれた文字を想定しています'h'

結果の要素を取得する(' ',rest)には、オプションの空白の後、その前に文字シーケンスが必要です'\'' : ' ' : '\'' : whatever。期待される入力にそれらがありません。そこでやりたいのは、の後のスペースを削除することです'-'。したがって、スペースがオプションであり、複数の文字の長さである可能性がある場合は、

readsMatrix ('-':s) = [(Nul, dropWhile isSpace s)]

動作します(改行、タブなども無視される場合)。必須スペースが1つだけ必要な場合、パターンは次のようになります。

readsMatrix ('-':' ':s) = [(Nul, s)]

2番目の方程式

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

から、PreludeHaskell構文にほぼ従って文字列をトークンに分割します。

ghci> lex "(12 + 34)  abc"
[("(","12 + 34)  abc")]

冒頭の括弧を分割し、余りからトークン"12"を分割します。

を使用する1つの可能性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ため、それでも成功しますが、イニシャルを変換した後も、入力の残りの部分にさらに4つの変換可能なトークンがあります。read'-'

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

一方、成功するはずです。

于 2012-11-25T19:01:44.930 に答える