Haskell:if-then-else解析错误

时间:2013-11-09 21:53:52

标签: parsing haskell if-statement

我正在研究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块以及一般代码的任何帮助。

2 个答案:

答案 0 :(得分:6)

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

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行。

(另外,由于它们之后只有一个语句,dothen之后的else s是多余的。)

答案 1 :(得分:1)

你说你有一些功能可以使用,但之后没有使用它们!也许我误会了。你的代码似乎混乱,似乎没有实现你想要的。你有一个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 -> String -> [(a, String)]。 第一个参数是优先级上下文;你可以把它想象成最后解析的东西的优先级。这个范围可以是0到11.默认值为0.对于像这样的简单解析,您甚至不使用它。对于更复杂(即递归)的数据类型,更改优先级上下文可能会更改解析。

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

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

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

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

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

如果返回列表是单个列表,其第二个值是空字符串,则解析被视为成功;即:某些[(x, "")]的{​​{1}}。空列表或其中任何剩余字符串不是空字符串的列表会给出错误x,并且包含多个值的列表会给出错误no parse