给定一个数据类型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
时,您可能想要使用satisfy
、isDigit
、read
和some
的组合(取决于您如何解决这个问题)。
一旦你完成了parseCI
,让CI
成为一个read实例就更简单了:
instance Read CI where
readsPrec _ = runParser parseCI