【发布时间】:2019-10-30 12:24:52
【问题描述】:
我正在尝试实现自己的 Applicative 解析器,这是我使用的代码:
{-# LANGUAGE ApplicativeDo, LambdaCase #-}
module Parser where
-- Implementation of an Applicative Parser
import Data.Char
import Control.Applicative (some, many, empty, (<*>), (<$>), (<|>), Alternative)
data Parser a = Parser { runParser :: String -> [(a, String)] }
instance Functor Parser where
-- fmap :: (a -> b) -> (Parser a -> Parser b)
fmap f (Parser p) = Parser (\s -> [(f a, s') | (a,s') <- p s])
instance Applicative Parser where
-- pure :: a -> Parser a
-- <*> :: Parser (a -> b) -> Parser a -> Parser b
pure x = Parser $ \s -> [(x, s)]
(Parser pf) <*> (Parser p) = Parser $ \s ->
[(f a, s'') | (f, s') <- pf s, (a, s'') <- p s']
instance Alternative Parser where
-- empty :: Parser a
-- <|> :: Parser a -> Parser a -> Parser a
empty = Parser $ \_s -> []
(Parser p1) <|> (Parser p2) = Parser $ \s ->
case p1 s of [] -> p2 s
xs -> xs
char :: Char -> Parser Char
char c = Parser $ \case (c':cs) | c == c' -> [(c,cs)] ; _ -> []
main = print $ runParser (some $ char 'A') "AAA"
当我运行它时,它会卡住并且永远不会返回。在深入研究问题后,我确定了根本原因是我实现了<|> 方法。如果我使用以下实现,那么一切都会按预期进行:
instance Alternative Parser where
empty = Parser $ \_s -> []
p1 <|> p2 = Parser $ \s ->
case runParser p1 s of [] -> runParser p2 s
xs -> xs
在我的理解中,这两个实现是完全等价的。我猜这可能与 Haskell 的惰性评估方案有关。有人可以解释发生了什么吗?
【问题讨论】:
-
另外,如果您使用的是列表而不是 Maybe,我希望您的
<|>实现是无条件地调用两个解析器,并将它们的结果与(++)结合起来;(Parser p) <|> (Parser q) = Parser $ (\s -> p s ++ q s) -
@amalloy:你是对的,但无论哪种方式,它都会陷入同一个循环。
-
@ShouYa 这不仅仅是“这两种不同的方法在概念上是相同的”,而是“你已经粘贴了两次完全相同的代码,并在问为什么它只能工作一次”。
-
@amalloy 哎呀我的错。固定:)
标签: parsing haskell infinite-loop lazy-evaluation alternative-functor