Haskell Parser Comminator Infinite循环

发布于 2025-02-10 22:54:26 字数 1116 浏览 1 评论 0原文

我试图通过Haskell编写一个简单的解析器,但仍在无限的循环中。 该代码是:

import Control.Applicative (Alternative, empty, many, (<|>))

data Parser a = Parser {runParser :: String -> [(a, String)]}

instance Functor Parser where
  fmap f (Parser p) = Parser $ \s -> [(f x', s') | (x', s') <- p s]

instance Applicative Parser where
  pure x = Parser $ \s -> [(x, s)]
  (Parser pf) <*> (Parser p) = Parser $ \s -> [(f' x, ss') | (f', ss) <- pf s, (x, ss') <- p ss]

instance Alternative Parser where
  empty = Parser $ \s -> []
  (Parser p1) <|> (Parser p2) = Parser $ \s ->
    case p1 s of
      [] -> p2 s
      xs -> xs

singleSpaceParser :: Parser Char
singleSpaceParser = Parser $ \s ->
  ( case s of
      x : xs -> if x == ' ' then [(' ', xs)] else []
      [] -> []
  )

multiSpaceParser :: Parser [Char]
multiSpaceParser = many singleSpaceParser

我只是将此文件加载到GHCI中,然后运行:

runParser multiSpaceParser "  123"

我希望它得到[(“”,“ 123”)],但实际上它有一个无限的循环,

我使用了跟踪来调试,看来许多是错误的,

我该如何修复此错误?

I'm trying to write a simple parser via Haskell, but stuck at an infinite loop.
the code is:

import Control.Applicative (Alternative, empty, many, (<|>))

data Parser a = Parser {runParser :: String -> [(a, String)]}

instance Functor Parser where
  fmap f (Parser p) = Parser $ \s -> [(f x', s') | (x', s') <- p s]

instance Applicative Parser where
  pure x = Parser $ \s -> [(x, s)]
  (Parser pf) <*> (Parser p) = Parser $ \s -> [(f' x, ss') | (f', ss) <- pf s, (x, ss') <- p ss]

instance Alternative Parser where
  empty = Parser $ \s -> []
  (Parser p1) <|> (Parser p2) = Parser $ \s ->
    case p1 s of
      [] -> p2 s
      xs -> xs

singleSpaceParser :: Parser Char
singleSpaceParser = Parser $ \s ->
  ( case s of
      x : xs -> if x == ' ' then [(' ', xs)] else []
      [] -> []
  )

multiSpaceParser :: Parser [Char]
multiSpaceParser = many singleSpaceParser

I just load this file in ghci, and run:

runParser multiSpaceParser "  123"

I expect it to get [(" ", "123")], but actually it got an infinite loop

I used trace to debug, and it seems that many is wrong

How can I fix this bug?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

寒冷纷飞旳雪 2025-02-17 22:54:27

让我们假设

many p = (:) <
gt; p <*> many p <|> pure []

并考虑呼叫

many singleSpaceParser "  123"

(在此处实际上并不重要,许多单身paceparser呼叫将始终循环。)

一个减少步骤的收益

   ((:) <
gt; singleSpaceParser <*> many singleSpaceParser <|> pure []) "  123"

现在观察到,为了将呼叫减少到( &lt; |&gt;),我们必须评估(&lt; |&gt;)的两个参数,属于Shape parser ...

让我们考虑为(:)&lt; $&gt;这样做。 SinglespaceParser&lt;*&gt;许多SinglesPaceParser。因为两个(&lt; $&gt;)(&lt;*&gt;)infixl 4,这是的应用程序&lt;*&gt;在最外层级别。

但是现在观察到,为了减少(&lt;*&gt;),我们再次必须评估>(&lt;*&gt;)的两个参数以达到形状解析器...,尤其是递归调用许多SinglesPaceParser

这是我们获得无限循环的地方。

通过将数据切换到newtype(或者,至少在所有第二个参数中避​​免在Parser构造器上积极地进行模式匹配),这些问题,这些问题)可以避免。

Let's assume

many p = (:) <
gt; p <*> many p <|> pure []

and consider the call

many singleSpaceParser "  123"

(The string does not actually matter here, the many singleSpaceParser call will always loop.)

One reduction step yields

   ((:) <
gt; singleSpaceParser <*> many singleSpaceParser <|> pure []) "  123"

Now observe that, in order to reduce the call to (<|>), we have to evaluate both arguments of (<|>) to be of the shape Parser ....

Let's consider doing that for (:) <$> singleSpaceParser <*> many singleSpaceParser. As both (<$>) and (<*>) are infixl 4, this is an application of <*> at the outermost level.

But now observe that in order to reduce (<*>), we again have to evaluate both arguments of (<*>) to be of the shape Parser ..., so in particular the recursive call many singleSpaceParser.

This is where we get the infinite loop.

By switching data to newtype (or alternatively, at least avoiding aggressively pattern-matching on the Parser constructor in all the second arguments), these problems can be avoided.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文