从头解析复数



给定一个数据类型data CI = CI Int Int,表示一个复数,我想为CI构建一个解析器,可以将"a"转换为CI a 0,将"(a,b)"转换为CI a b。例如,我想要一个函数parseCI,这样runParser parseCI "(1,2)"返回值[(CI 1 2, "")](理想情况下,类似的东西也可以)。我还想让CI成为read的一个实例。

我想使用下面代码中的函数和定义(基本上,没有任何高级功能,如Parsec),但我不确定从哪里开始。一些起始代码让我走上正确的轨道和/或提示将是有帮助的。我不想要一个完整的答案,因为我想自己弄清楚。

module Parser where
import Control.Applicative
import Control.Monad
newtype Parser a = Parser { runParser :: String -> [(a,String)] }
satisfy :: (Char -> Bool) -> Parser Char
satisfy f = Parser $ s -> case s of
    [] -> []
    a:as -> [(a,as) | f a]
char :: Char -> Parser Char
char = satisfy . (==)
string :: String -> Parser String
string str = Parser $ s -> [(t,u) | let (t,u) = splitAt (length str) s, str == t]
instance Functor Parser where
    fmap f p = Parser $ s ->
        [ (f a,t)
        | (a,t) <- runParser p s
        ]
instance Applicative Parser where
    pure a = Parser $ s -> [(a,s)]
    af <*> aa = Parser $ s ->
        [ (f a,u) 
        | (f,t) <- runParser af s
        , (a,u) <- runParser aa t
        ]
instance Alternative Parser where
    empty = Parser $ s -> []
    p1 <|> p2 = Parser $ (++) <$> runParser p1 <*> runParser p2`
instance Monad Parser where
    return = pure
    ma >>= f = Parser $ s ->
       [ (b,u) 
       | (a,t) <- runParser ma s
       , (b,u) <- runParser (f a) t
       ]
instance MonadPlus Parser where
    mzero = empty
    mplus = (<|>)

您可能已经见过它,但如果您还没有见过:Haskell中的一元解析设置了这样的解析。

由于您有两种不同的解析CI的方法,您可能希望将其作为两个问题来处理:使一个解析器parseCI1"a"解析为CI a 0,并使另一个解析器parseCI2"(a,b)"解析为CI a b。然后,您可以使用

将这些组合为一个
parseCI = parseCI1 <|> parseCI2

对于这两个子解析器,您将需要某种解析整数的方法:parseInt :: Parser Int。在制作parseInt时,您可能想要使用satisfyisDigitreadsome的组合(取决于您如何解决这个问题)。


一旦你完成了parseCI,让CI成为一个read实例就更简单了:

instance Read CI where
  readsPrec _ = runParser parseCI

相关内容

  • 没有找到相关文章

最新更新