0

我正在研究 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 语句的结构基于此问题中使用的结构:Haskell if-then-else conditional 中的“parse error on input”

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

4

2 回答 2

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

(另外,因为它们后面只有一个语句,所以后面的dosthenelse是多余的。)

于 2013-11-09T21:59:24.347 回答
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, "")]对于一些x。空列表或任何剩余字符串都不是空字符串的列表会给出错误no parse,而具有多个值的列表会给出错误ambiguous parse

于 2013-11-10T12:07:23.627 回答