我试图了解这是怎么回事:
satisfy :: (Char -> Bool) -> Parser Char
satisfy pred = PsrOf p
where
p (c:cs) | pred c = Just (cs, c)
p _ = Nothing
等效于此:
satisfy :: (Char -> Bool) -> Parser Char
satisfy pred = do
c <- anyChar
if pred c then return c else empty
这是有关Haskell解析的一些讲义的摘录,我正试图理解:
import Control.Applicative
import Data.Char
import Data.Functor
import Data.List
newtype Parser a = PsrOf (String -> Maybe (String, a))
-- Function from input string to:
--
-- * Nothing, if failure (syntax error);
-- * Just (unconsumed input, answer), if success.
dePsr :: Parser a -> String -> Maybe (String, a)
dePsr (PsrOf p) = p
-- Monadic Parsing in Haskell uses [] instead of Maybe to support ambiguous
-- grammars and multiple answers.
-- | Use a parser on an input string.
runParser :: Parser a -> String -> Maybe a
runParser (PsrOf p) inp = case p inp of
Nothing -> Nothing
Just (_, a) -> Just a
-- OR: fmap (\(_,a) -> a) (p inp)
-- | Read a character and return. Failure if input is empty.
anyChar :: Parser Char
anyChar = PsrOf p
where
p "" = Nothing
p (c:cs) = Just (cs, c)
-- | Read a character and check against the given character.
char :: Char -> Parser Char
-- char wanted = PsrOf p
-- where
-- p (c:cs) | c == wanted = Just (cs, c)
-- p _ = Nothing
char wanted = satisfy (\c -> c == wanted) -- (== wanted)
-- | Read a character and check against the given predicate.
satisfy :: (Char -> Bool) -> Parser Char
satisfy pred = PsrOf p
where
p (c:cs) | pred c = Just (cs, c)
p _ = Nothing
-- Could also be:
-- satisfy pred = do
-- c <- anyChar
-- if pred c then return c else empty
instance Monad Parser where
-- return :: a -> Parser a
return = pure
-- (>>=) :: Parser a -> (a -> Parser b) -> Parser b
PsrOf p1 >>= k = PsrOf q
where
q inp = case p1 inp of
Nothing -> Nothing
Just (rest, a) -> dePsr (k a) rest
我了解直到Monad定义的最后一点的所有内容,特别是我不理解下面的行如何返回Parser b
定义所需的(>>=)
类型的内容:
Just (rest, a) -> dePsr (k a) rest
如果没有示例,我很难理解Monad的定义。值得庆幸的是,我们在satisfy
函数的替代版本中有了一个,它使用了do表示法(这当然意味着正在调用Monad)。我真的还不知道do-notation,所以这里是satisfy
的简化版本:
satisfy pred = do
anyChar >>= (c ->
if pred c then return c else empty)
因此,根据我们的(>>=)
定义的第一行,即
PsrOf p1 >>= k = PsrOf q
我们以anyChar
为PsrOf p1
,以(c -> if pred c then return c else empty)
为k
。我没有得到的是dePsr (k a) rest
在(k a)
中如何返回Parser
(至少它是固定的,否则调用dePsr
毫无意义)。 rest
的存在使情况更加混乱。即使(k a)
返回了Parser
,调用dePsr
也会从返回的Parser
中提取基础函数,并将rest
作为输入传递给它。绝对不会返回Parser b
的定义所要求的(>>=)
类型的内容。显然我误解了某个地方。
答案 0 :(得分:3)
好的,也许这会有所帮助。让我们首先将一些要点放回dePsr
中。
dePsr :: Parser a -> String -> Maybe (String, a)
dePsr (PsrOf p) rest = p rest
让我们也写出return :(注意,为了清楚起见,我将所有观点都放在这里)
return :: a -> Parser a
return a = PsrOf (\rest -> Just (rest, a))
现在从Just
定义的(>>=)
分支开始
Just (rest, a) -> dePsr (k a) rest
让我们确保我们对每件事物都表示同意:
rest
应用了p1
后未解析的字符串a
应用p1
k :: a -> Parser b
获取前一个解析器的结果并创建一个新的解析器dePsr
将Parser a
退回到函数`String-> Maybe(String,a)请记住,我们会将 back 再次包装到函数顶部的解析器中:PsrOf q
因此,在英语中,绑定(>>=)
在a
中使用一个解析器,在a
中使用一个从b
到函式的函数,并在b
中返回一个解析器。通过将q :: String -> Maybe (String, b)
包装在解析器构造函数PsrOf
中来生成结果解析器。然后,组合分析器q
取一个名为String
的{{1}}并将从模式匹配中获得的函数inp
应用于第一个解析器,并对结果进行模式匹配。对于错误,我们传播p1 :: String -> Maybe (String,a)
(很容易)。如果第一个解析器有一个结果,我们必须拖曳多条信息,仍然是未解析的字符串称为Nothing
和结果rest
。我们将a
赋予第二个解析器组合器a
,并得到一个k
,我们需要将其与Parser b
拆开以得到一个函数(dePsr
。该功能可以应用于String -> Maybe (String,b)
,以获得组合解析器的最终结果。
我认为阅读此书最难的部分是有时我们curry解析器函数会掩盖实际发生的事情。
好,rest
示例
satisfy
satisfy pred
= anyChar >>= (c -> if pred c then return c else empty)
来自备用实例,并且为empty
,因此解析器始终失败。
让我们只看成功的分支。通过仅替换成功部分:
PsrOf (const Nothing)
因此在绑定PsrOf (\(c:cs) ->Just (cs, c)) >>= (\c -> PsrOf (\rest -> Just (rest, c)))
定义中
(>>=)
p1 = \(c:cs -> Just (cs, c))
k = (\c -> PsrOf (\rest -> Just (rest, c)))
再次仅成功分支然后q inp = let Just (rest,a) = p1 inp in dePsr (k a) rest
变成
q
进行一些β-还原
q inp =
let Just (rest, a) = (\(c:cs) -> Just (cs, c)) inp
in dePsr (\c -> PsrOf (\rest -> Just (rest, c))) a rest
最后清理更多
q inp =
let (c:cs) = inp
rest = cs
a = c
in dePsr (PsdOf (\rest -> Just (rest, a))) rest -- dePsr . PsrOf = id
因此,如果q (c:cs) = Just (cs, c)
成功,我们将pred
还原为恰好我们期望的satisfy
,和正是我们在第一个示例中找到的这个问题。我将其保留为原样,然后让读者(阅读:我很懒)证明,如果anyChar
或inp = ""
的结果与第一个{{1 }}示例。
注意:如果您正在执行除类分配以外的任何其他操作,则从一开始就通过错误处理开始,使解析器pred c = False
易于使错误类型更多,这将为您节省数小时的痛苦和沮丧。一般情况下,但PITA会将所有内容从Nothing
更改为satisfy
。
问题:“ [C]您要解释一下将“点”放回去后如何从String -> Either String (String,a)
到达Maybe
吗?
答案:首先,很不幸,给出Monad实例定义而没有Functor和Applicative定义。 Either
和return a = PsrOf (\rest -> Just (rest, a))
函数必须相同(这是Monad法则的一部分),并且它们将被称为同一事物,只是return = pure
在Haskell历史上很早就适用。实际上,我不“知道”纯净的外观,但我知道它的含义,因为它是唯一可能的定义。 (如果您想了解该陈述的证明,我已经阅读了论文,并且知道了结果,但是我对输入Lambda演算的了解不足,不足以对再现结果有信心。)
pure
必须在上下文中包装一个值,而不能更改上下文。
return
Monad
是一个函数,该函数接受要解析的字符串并返回return
值以及原始字符串的任何未解析部分或返回return :: Monad m => a -> m a
return :: a -> Parser a -- for our Monad
return :: a -> PsrOf(\str -> Maybe (rest, value)) -- substituting the constructor (PSUDO CODE)
PsrOf Parser
`。解析器总是成功,因此我们必须返回Just值。
Just
Nothing on failure, all wrapped in the constructor
是上下文,它未经更改地传递。
. The context is the string to be parsed, so we cannot change that. The value is of course what was passed to
是我们放在Monad上下文中的值。
出于完整性考虑,这也是Functor对return a = PsrOf (\rest -> Just (rest, a))
的唯一合理定义。
rest