【问题标题】:Haskell: if-then-else parse errorHaskell:if-then-else 解析错误
【发布时间】:2013-11-21 21:48:53
【问题描述】:

我正在处理 Read ComplexInt 的一个实例。

这是给出的:

data ComplexInt = ComplexInt Int Int
deriving (Show)

module Parser (Parser,parser,runParser,satisfy,char,string,many,many1,(+++)) where

import Data.Char
import Control.Monad
import Control.Monad.State

type Parser = StateT String []

runParser :: Parser a -> String -> [(a,String)]
runParser = runStateT

parser :: (String -> [(a,String)]) -> Parser a
parser = StateT

satisfy :: (Char -> Bool) -> Parser Char
satisfy f = parser $ \s -> case s of
    [] -> []
    a:as -> [(a,as) | f a]

char :: Char -> Parser Char
char = satisfy . (==)

alpha,digit :: Parser Char
alpha = satisfy isAlpha
digit = satisfy isDigit

string :: String -> Parser String
string = mapM char

infixr 5 +++
(+++) :: Parser a -> Parser a -> Parser a
(+++) = mplus

many, many1 :: Parser a -> Parser [a]
many p = return [] +++ many1 p
many1 p = liftM2 (:) p (many p)

这是给定的练习:

"Use Parser to implement Read ComplexInt, where you can accept either the simple integer 
syntax "12" for ComplexInt 12 0 or "(1,2)" for ComplexInt 1 2, and illustrate that read 
works as expected (when its return type is specialized appropriately) on these examples. 
Don't worry (yet) about the possibility of minus signs in the specification of natural 
numbers."

这是我的尝试:

data ComplexInt = ComplexInt Int Int
    deriving (Show)

instance Read ComplexInt where
    readsPrec _ = runParser parseComplexInt

parseComplexInt :: Parser ComplexInt
parseComplexInt = do
    statestring <- getContents
    case statestring of
        if '(' `elem` statestring 
            then do process1 statestring
            else do process2 statestring
    where
    process1 ststr = do
        number <- read(dropWhile (not(isDigit)) ststr) :: Int
        return ComplexInt number 0
    process2 ststr = do
        numbers <- dropWhile (not(isDigit)) ststr
        number1 <- read(takeWhile (not(isSpace)) numbers) :: Int
        number2 <- read(dropWhile (not(isSpace)) numbers) :: Int
        return ComplexInt number1 number2

这是我的错误(我当前的错误,因为我确信一旦我解决了这个错误,还会有更多错误,但我会逐步采取这一步骤):

Parse error in pattern: if ')' `elem` statestring then
                            do { process1 statestring }
                        else
                            do { process2 statestring }

我的 if-then-else 语句结构基于此问题中使用的结构:"parse error on input" in Haskell if-then-else conditional

如果您发现任何明显的错误,我将不胜感激有关 if-then-else 块以及一般代码的任何帮助。

【问题讨论】:

  • 这里还有几个错误,您很快就会处理:not(isDigit)not(isSpace) 应该是 not . isDigitnot . isSpacedo&lt;- 符号仅适用于单子,但您似乎只想要 let ... in

标签: parsing haskell if-statement


【解决方案1】:

让我们看看解析错误周围的代码。

case statestring of
    if '(' `elem` statestring 
        then do process1 statestring
        else do process2 statestring

case 不是这样工作的。它应该像这样使用:

case statestring of
    "foo"  ->  -- code for when statestring == "foo"
    'b':xs ->  -- code for when statestring begins with 'b'
    _      ->  -- code for none of the above

由于您没有实际使用case,因此完全去掉case 行。

(另外,由于它们后面只有一个语句,thenelse 之后的 dos 是多余的。)

【讨论】:

    【解决方案2】:

    您说您获得了一些可以使用的功能,但后来没有使用它们!也许我误解了。你的代码看起来很混乱,似乎没有达到你想要的效果。你调用了getContents,它的类型为IO String,但该函数应该在解析器monad中,而不是io monad中。

    如果您真的想使用它们,方法如下:

    readAsTuple :: Parser ComplexInt
    readAsTuple = do
      _ <- char '('          
      x <- many digit
      _ <- char ','
      y <- many digit
      _ <- char ')'
      return $ ComplexInt (read x) (read y)
    
    readAsNum :: Parser ComplexInt
    readAsNum = do
      x <- many digit
      return $ ComplexInt (read x) 0
    
    instance Read ComplexInt where
      readsPrec _ = runParser (readAsTuple +++ readAsNum)
    

    这是相当基本的,因为像" 42"(带有空格的字符串)这样的字符串会失败。

    用法:

    > read "12" :: ComplexInt 
    ComplexInt 12 0
    > read "(12,1)" :: ComplexInt
    ComplexInt 12 1
    

    Read 类型类有一个名为readsPrec 的方法;定义此方法足以完全定义类型的读取实例,并自动为您提供函数read

    什么是readsPrec

    readsPrec :: Int -&gt; String -&gt; [(a, String)]。 第一个参数是优先上下文;您可以将其视为最后解析的内容的优先级。这可以从 0 到 11。默认值为 0。对于像这样的简单解析,您甚至不使用它。对于更复杂(即递归)的数据类型,更改优先上下文可能会更改解析。

    第二个参数是输入字符串。

    输出类型是可能的解析和剩余的字符串解析终止。例如:

    >runStateT (char 'h') "hello world"
    [('h',"ello world")]
    

    请注意,解析是不确定的;返回每个匹配的解析。

    >runStateT (many1 (char 'a')) "aa"
    [("a","a"),("aa","")]
    

    如果返回列表是第二个值为空字符串的单例列表,则认为解析成功;即:[(x, "")] 一些x。空列表或任何剩余字符串都不是空字符串的列表会给出错误no parse,而具有多个值的列表会给出错误ambiguous parse

    【讨论】:

    • 谢谢!代码现在可以按照您的指示编译。但是,我也对如何使用此代码感到困惑。如何测试输入“12”将返回 ComplexInt 12 0,以及输入 (12,10) 将返回 ComplexInt 12 10?
    • @user2967411 编辑了答案,回复了您的问题以及一些额外信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    • 2015-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多