Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Json解析器,错误地将字符串解析为数字_Json_Haskell_Parsec - Fatal编程技术网

Json解析器,错误地将字符串解析为数字

Json解析器,错误地将字符串解析为数字,json,haskell,parsec,Json,Haskell,Parsec,一般来说,我对Haskell和函数式编程还是相当陌生的,所以我正在用Parsec编写一个小程序来解析JSON,并将其打印出来,作为学习基本概念的一种手段。这就是我到目前为止所做的: import Text.Parsec import Text.Parsec.String data JValue = JString String | JNumber Double | JBool Bool | JNull

一般来说,我对Haskell和函数式编程还是相当陌生的,所以我正在用Parsec编写一个小程序来解析JSON,并将其打印出来,作为学习基本概念的一种手段。这就是我到目前为止所做的:

import Text.Parsec
import Text.Parsec.String

data JValue = JString String
            | JNumber Double
            | JBool Bool
            | JNull
            | JObject [(String, JValue)]
            | JArray [JValue]
              deriving (Eq, Ord, Show)

parseJString, parseJNumber, parseJBool, parseJNull :: Parser JValue
parseJString = do
    str <- between (char '"') (char '"') (many (noneOf "\""))
    return . JString $ str

parseJNumber = do
    num <- many digit
    return . JNumber . read $ num

parseJBool = do
    val <- string "true" <|> string "false"
    case val of
        "true"  -> return (JBool True)
        "false" -> return (JBool False)

parseJNull = string "null" >> return JNull

parseJValue :: Parser JValue
parseJValue =   parseJString 
            <|> parseJNumber 
            <|> parseJBool 
            <|> parseJNull
然而,当我尝试解析
true
false
null
时,
parseJValue
失败,我得到了一个有趣的错误

ghci> parse parseJValue "test" "true"
Right (JNumber *** Exception: Prelude.read: no parse

我得到了一个成功的解析,但是解析返回一个
JNumber
,后面是一个错误,表明Prelude.read失败。我觉得我在构建解析器时遗漏了一些核心概念,但我看不出哪里出了问题。此外,我的代码是否犯了初学者的错误,即这些错误是否会被视为“坏”haskell?

问题是在
parseJNumber
中使用了
many
。当没有使用以下字符串的任何字符时,它也是一个有效的解析(“许多p应用解析器p零次或多次。[…]”)。您需要的是
many1

parseJNumber = do
  num <- many1 (oneOf "0123456789")
  return $ JNumber (read num :: Double)
此外,还可以通过重构
parseJBool
,消除代码重复:

parseJBool = do
  val <- string "true" <|> string "false"
  return (case val of
    "true"  -> JBool True
    "false" -> JBool False)

您应该尝试使用
多个1位数
,而不是
多个位数
many
在零次出现参数时成功

比较:

ghci> parse (many digit) "test" "true"
Right ""
ghci> parse (many1 digit) "test" "true"
unexpected "t"
expecting digit

因此,在您的情况下,
parseJValue
中的
parseJNumber
将成功并返回一个空字符串,然后将该字符串传递给
read
。但是
read”“::Double
失败。

我完全同意您建议的重构更改,但是vim中的Syntastic给了我一个相当模糊的警告,每当我使用
>=return
时,应该使用
liftM
。有什么理由改变它,还是应该忽略它?您可能应该使用
liftM f x
,它与
x>>=return完全相同。f
。或者更好的是,您可以使用
fmap fx
(只要有
Functor
实例就可以使用)或
fx
(只要有
Applicative
实例就可以使用)。
parseJBool = do
  val <- string "true" <|> string "false"
  return (case val of
    "true"  -> JBool True
    "false" -> JBool False)
parseJBool = (string "true" <|> string "false") >>= return . toJBool
 where
  -- there are only two possible strings to pattern match
  toJBool "true" = JBool True
  toJBool _      = JBool False
-- additionally, you do not need an extra type signature for `read`
-- the constructor `JNumber` already infers the correct type
parseJNumber =
  many1 (oneOf "0123456789") >>= return . JNumber . read

parseJString =
  between (char '"') (char '"') (many (noneOf "\"")) >>= return . JString
ghci> parse (many digit) "test" "true"
Right ""
ghci> parse (many1 digit) "test" "true"
unexpected "t"
expecting digit