从头开始解析复数
Parsing a Complex Number from Scratch
给定一个数据类型 data CI = CI Int Int
,表示一个复数,我想为 CI 构建一个解析器,可以将 "a"
转换为 CI a 0
和 "(a,b)"
到 CI a b
。例如,我想要一个函数 parseCI
这样 runParser parseCI "(1,2)"
returns 值 [(CI 1 2, "")]
(理想情况下,但类似的东西很好)。我还想使 CI 成为 read
的一个实例。
我想使用下面代码中的函数和定义来执行此操作(基本上,没有任何高级功能,例如 Parsec),但我不确定从哪里开始。一些让我走上正轨的起始代码 and/or 提示会很有帮助。我不是在寻找完整的答案,因为我想自己弄清楚。
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 = (<|>)
您可能已经看过它,但如果您还没有看过:Monadic Parsing in Haskell 像这样设置解析。
由于您有两种不同的解析方式 CI
,您可能希望将其作为两个问题来处理:创建一个解析器 parseCI1
将 "a"
解析为 CI a 0
并制作另一个解析器 parseCI2
将 "(a,b)"
解析为 CI a b
。然后,您可以将它们合二为一
parseCI = parseCI1 <|> parseCI2
对于这两个子解析器,您将需要一些解析整数的方法:parseInt :: Parser Int
。在制作 parseInt
时,您可能希望使用 satisfy
、isDigit
, read
, and possibly some
的某种组合(取决于您如何解决这个问题)。
在完成 parseCI
后,将 CI
设为 read 的实例会更简单一些:
instance Read CI where
readsPrec _ = runParser parseCI
给定一个数据类型 data CI = CI Int Int
,表示一个复数,我想为 CI 构建一个解析器,可以将 "a"
转换为 CI a 0
和 "(a,b)"
到 CI a b
。例如,我想要一个函数 parseCI
这样 runParser parseCI "(1,2)"
returns 值 [(CI 1 2, "")]
(理想情况下,但类似的东西很好)。我还想使 CI 成为 read
的一个实例。
我想使用下面代码中的函数和定义来执行此操作(基本上,没有任何高级功能,例如 Parsec),但我不确定从哪里开始。一些让我走上正轨的起始代码 and/or 提示会很有帮助。我不是在寻找完整的答案,因为我想自己弄清楚。
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 = (<|>)
您可能已经看过它,但如果您还没有看过:Monadic Parsing in Haskell 像这样设置解析。
由于您有两种不同的解析方式 CI
,您可能希望将其作为两个问题来处理:创建一个解析器 parseCI1
将 "a"
解析为 CI a 0
并制作另一个解析器 parseCI2
将 "(a,b)"
解析为 CI a b
。然后,您可以将它们合二为一
parseCI = parseCI1 <|> parseCI2
对于这两个子解析器,您将需要一些解析整数的方法:parseInt :: Parser Int
。在制作 parseInt
时,您可能希望使用 satisfy
、isDigit
, read
, and possibly some
的某种组合(取决于您如何解决这个问题)。
在完成 parseCI
后,将 CI
设为 read 的实例会更简单一些:
instance Read CI where
readsPrec _ = runParser parseCI