将[Char]与Read实例匹配

时间:2014-02-17 22:00:52

标签: haskell instance

我遇到了用户定义的数据结构和Read

实例的问题

首先是我的数据结构

data Nat = Null | Succ Nat

我的read函数定义如下

readNatNat :: String -> Nat
readNatNat xs   |first == '(' = readNatNat (tail xs)
                |first == ' ' = readNatNat (tail xs)
                |firstFour == "Succ" = (Succ (readNatNat(drop 4 xs)))
                |firstFour == "Null" = Null
                |b `elem` [0..] = toNatInt(b)
                 where b = (read(xs)::Int)
                    first = head xs
                    firstFour = take 4 xs

toNatInt :: Int -> Nat
toNatInt x | x==0 = Null
       | x<0 = error "Unter Null gibts kein Int 2 Nat"
       | otherwise = Succ(toNatInt(x-1))

readNatNat作为独立函数按预期工作,但是当我想将它与Read实例一起使用并尝试加载它时,我得到以下异常

Couldn't match type `[Char]' with `Int'
Expected type: Int -> ReadS Nat
  Actual type: String -> Nat
In the expression: readNatNat
In an equation for `readsPrec': readsPrec = readNatNat
In the instance declaration for `Read Nat'
Failed, modules loaded: none.

EnumEqShow等其他实例进行编程非常简单。 我理解Read期望一个Int,但我不知道为什么;)

我认为Readshow相反,并使用String并将其解析为我的数据结构。

我像这样定义了Show

instance Show Nat where
     show (Succ a) = showsRealSucc(Succ a)
     show Null = show "Null"

showsRealSucc :: Nat -> String
showsRealSucc Null = "Null"
showsRealSucc (Succ a) = (if a/= Null then ("Succ ("++showsRealSucc(a)++")") 
                                      else "Succ Null")

了解Nat

它是自然数的表示,所以当我输入readNatNat "3"输出时 "(Succ(Succ(Succ Null)))"

提前致谢!!

1 个答案:

答案 0 :(得分:2)

readsPrec采用优先级参数

readsPrec    :: Int -> ReadS a
type ReadS a = String -> [(a,String)]

所以

readsPrec    :: Int -> String -> [(a,String)]

readsPrec需要额外的Int参数来指示周围上下文中的优先级,因此,例如,如果您正在阅读Just Null,则周围的优先级为10,即函数应用程序。

在您的情况下,我认为您可以使用效用函数readParen :: Bool -> ReadS a -> ReadS a来处理:

instance Read Nat where
readsPrec prec = readParen prec natReads where
  natReads xs = [(readNatNat xs,"")]

readsPrec应该是迷你解析器

  1. 如果解析失败,则应返回[],不要导致错误。
  2. 您应该在剩余的String部分
  3. 中返回剩余的输入
  4. (应该有可能的解析列表)
  5. 第3。成功列表和
    2.退还剩余部分:

    这种解决方法不适用于readsPrec应该如何工作。列表[(a,String)]应包含可能的解析和余数字符串,以便readsPrec 5 "Null,Null,Succ Null]"应为[(Null, ",Null,Succ Null]" ]。正如我试图在那里暗示的那样,在解析值列表等时可以使用剩余的未消耗输入。

    重要的:
    如果你不这样做,你更有可能从read "[Null,Succ Null]"得到错误的结果。

    很少会有多个可能的解析,但是,例如,如果您决定将Nat值显示为"0+1+1",则需要为该输入生成列表{ {1}}

    这意味着您应该检查进出方式的括号数量,以便在余数字符串中返回适当的金额:[(Null,"+1+1"), (Succ Null, "+1"), (Succ (Succ Null), "") ]应为readsPrec 3 "((Succ (Succ Null)))))"

    <强> 1。不要导致错误,请返回[]

    如果输入不是有效的Nat,则需要返回[(Succ (Succ Null), "))"]。我发现了三个问题:

    []

    您应该将这些转换为支票,然后回答 where b = (read(xs)::Int) -- could crash first = head xs -- could crash | x<0 = error "Unter Null gibts kein Int 2 Nat" -- crashes

    递归解析

    可悲的是,我认为这意味着你需要在某种程度上重写解析器。好消息是它本质上是递归的,[]可以为你完成大部分工作。一个有用的辅助函数是

    readParen False

    这样,当您找到fmapPairs :: (a -> b) -> [(a,String)] -> [(a,String)] fmapPairs f pairs = [(f a,xs) | (a,xs) <- pairs] 时,可以fmapPairs Succ (readParen False)余下的"Succ"。让我知道你是否需要更多的提示来完成这项工作(我认为你已经证明了你的能力,而且现在你知道更多关于readsPrec的信息,这是一个很好的练习。)