我有这个解析器定义:
newtype Parser a = P { getParser :: String -> Maybe (a, String) }
我也有这个功能:
functionMatching :: Parser (Char, String, Char)
functionMatching = (,,) <$> spot (`elem` "\\") <*> getWord <*> spot (`elem` ".")
如何使下面的函数从(Char,String Char)返回中间元素
functionCons:: Parser (Char, String, Char) -> (String, String)
functionCons = undefined
示例:
getParser functionMatching "\\x.y"
Just (('\\',"x",'.'),"y")
我想提取x和y。
谢谢!
答案 0 :(得分:4)
这将是:
functionCons (P p) = P $ fmap (\((_,x,_), s) -> (x,s)) . p
但我建议您声明Parser a
一个仿函数:
newtype Parser a = P { getParser :: String -> Maybe (a, String) }
deriving (Functor)
functionCons :: Parser (a,b,c) -> Parser b
functionCons = fmap (\(_,x,_) -> x)
这当然不是Parser (a,b,c) -> (b, String)
,但你需要看看你是否真的需要unJust
结果。例如:
functionCons :: Parser (a,b,c) -> String -> (b,String)
functionCons = unJust . fmap (\(_,x,_) -> x) where
unJust (P p) s = case p s of
Just x -> x
-- if Nothing happens, it will throw a unmatched pattern
正如评论中正确指出的那样,类型与请求的类型不同。如果没有Parser (a,b,c) -> (b, String)
提供要解析的functionCons
,则无法获得确切的签名String
。
答案 1 :(得分:2)
这里有两个问题:
我认为你没有在haskell允许的范围内使用applicatives。 functionMatching
返回分隔符,只是稍后将它们丢弃。要丢弃Parser匹配项,请使用*>
和<*
:
functionMatching''' :: Parser String
functionMatching''' = spot (`elem` "\\") *> getWord <* spot (`elem` ".")
functionCons''' :: String -> (String, String)
functionCons''' = fromJust . getParser functionMatching'''
<*>
,*>
和<*
如何工作很容易记住,他们只返回>
和<
指向的内容:
f <*> x: f x
x *> y: y
x <* y: x
有一种更惯用的方式来使用解析器。请勿提取未解析的String
Maybe (a,String)
,解析!这是对getWord
的另一次调用。
functionMatching' :: Parser (String, String)
functionMatching' = (,) <$> (spot (=='\\') *> getWord <* spot (=='.')) <*> getWord
functionCons' :: String -> (String, String)
functionCons' = fromJust . evalParser functionMatching'
-- evalParser discards the unparsed String
evalParser :: Parser a -> String -> Maybe a
evalParser p s = fst <$> getParser p s
evalParser
是一个类似于您想要导出的runState, evalState, execState的有用功能。
解析器的想法是不要过早地离开解析器。如果没有匹配,上面的代码将遇到错误(fromJust
执行此操作)。 haskell为此使用了Maybe
,并且已经在解析器中内置了Maybe
。我不知道你想用(String,String)
做什么,但你可能想把它传递给函数f :: String -> String -> b
。
functionMatching'' :: Parser b
functionMatching'' = f <$> (spot (=='\\') *> getWord <* spot (=='.')) <*> getWord
会更好地处理不匹配问题。