haskell 兆秒差距..在遇到不同元素之前找到一个元素

hrirmatl  于 2022-11-30  发布在  其他
关注(0)|答案(1)|浏览(131)

我正在尝试解析(使用magaparsec)FreePlane(思维Map器)的XML导出。
这是我第三次尝试真正"学习"(内化)百万秒差距。我以前写过几个解析器,两个成功了(经过了很多努力),还有一个我放弃了,手动解析。我一定是错过了一些基本概念......在这个方向上的帮助将不胜感激
执行摘要:
我需要一个解析器,这样做..

  • 在找到第二件事之前先找到第一件事。
  • 如果没有第一件事,在你得到第二件事之前生成错误
  • 不要错过第二件事

我可能没有解释清楚..
这里是当前的具体问题..
An XML section is given below (dots arbitray characters) which shows two attribute 'chunks' each chunk surrounded by "< .. >"
... <... LINK="http://www.a.com" ... >...<... LINK="http://b.abc.com > ...
问题在于'LINK'属性是可选的。
"块"有大量的属性,所以我不知道如何单独解析它们..所以我随意跳过它们,只关心"链接"属性。
所以,如果我在上面的部分做天真的事情。

manyTill asciiChar (string "LINK=\""
   text = manyTill asciiChar (char '"')
   return text

它工作得很好。
http://www.a.com
返回第一个块的链接。
但是,如果第一个块中缺少"LINK"属性(如下所示
...<..... >...<... LINK="http://b.abc.com > ...
我的简单解析器将返回,
http://b.abc.com
这是不正确。
我希望它出错(这样以后我就可以使用〈|〉)
这是一个3天的这个问题尝试不同的方法..包括只是试图找到一个解析器做我想要的..
例如,在找到第二件事("〉")之前找到第一件事(链接)。如果不是错误。
最后我放弃了,尝试了老式的方法,即提取"〈〉"之间的文本,然后使用普通的列表函数来提取链接名称,如果找不到就失败。
下面是代码。

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoMonomorphismRestriction #-}

import Data.List (find, isInfixOf, isPrefixOf)
import Data.Maybe
import qualified Data.Set as Set
import Data.Void
import Text.Megaparsec
import Text.Megaparsec.Char

type Parser = Parsec Void String

--------------------------------
-- Skip a Chunk with no LINK  --
--------------------------------
skipChunk :: Parser String
skipChunk = do
  _ <- manyTill asciiChar (char '<')
  _ <- manyTill asciiChar (char '>')
  return ""

------------------------------------
-- Extrack LINK old fashioned way --
------------------------------------
linkName :: String -> String
linkName text =
  let ws = words text
   in fromJust $ find (\w -> isPrefixOf "LINK=" w) ws

----------------
-- Parse Link --
----------------
parseLink :: Parser String
parseLink = do
  _ <- lookAhead (manyTill asciiChar (char '<')) -- make sure to not consume input
  text <- lookAhead (manyTill asciiChar (char '>')) -- capture chunk
  name <-
    if (isInfixOf "LINK=\"" text) -- extract link old fashioned way
      then return $ linkName text
      else (failure Nothing Set.empty)
  return name

-----------------
-- Base Parser --
-----------------
baseParser :: Parser String
baseParser = do
  parseLink <|> skipChunk -- First parser fails, skips the chunk
  parseLink -- Yields LINK from second chunk

----------
-- Main --
----------
main :: IO ()
main = do
  result <- parseTest baseParser "...<.. INK=\"abc\" ... > ... <... LINK=\"http://b.abc.com > ..."
  putStrLn $ show result

这实际上是可行的
"LINK="http://b.abc.com"
但是哇...这是丑陋的!!!
所以,
1.是否有一个解析器来"在找到第二件事之前找到第一件事..如果没有错误"
1.如果你是一个字符一个字符地读,并且你的语法是很好定义和众所周知的,那么看起来兆秒差距是很好的。跳过任意的部分似乎是有问题的...这是真的吗?
1.我喜欢Haskell,但如果有人能给我推荐一本关于Visual Basic的好书,我会很感激,因为这似乎是我的智力极限。
先谢谢你,
汤姆

qyuhtwio

qyuhtwio1#

很抱歉我的回答很简短
1.问题是manyTill asciiChar (string "LINK=\"")不正确。不应允许所有的asciiChar!特别是,不应允许>,因为它应该标记当前块的结尾
1.尝试用这种风格(简单的字符串搜索)编写XML解析器可能不会很顺利,因为XML太复杂了,字符串搜索无法有效处理。
如果您的目标是解析XML,我建议您使用现有的XML解析库。如果您的目标是学习Megaparsec,我建议您从更一般的Angular 来解决这个问题:而不是“我如何提取LINK属性?”,而是“我如何解析标记?我如何解析属性?结果类型应该是什么样的?”正确地实现一个提取LINK属性的程序本质上需要考虑XML的所有复杂性,因此最好只做一个通用的解析器。
此外,如果您正在尝试学习Megaparsec,XML是一个相当大的一块开始咬。下面是一些中间问题,如果它们对您有用的话,每个问题都应该有助于编写XML解析器:解析整数、解析字符串(Haskell语法)、解析字符串元组(("a", "b c", "d")等)、解析字符串元组和嵌套元组(("a", ("b c", "d", ("e", "f gh i"), "j", ("k l", "m")))等)。
编辑--回应你的评论“因为那似乎是我的智力极限”。Haskell很难,解析也很难。你在这个问题上挣扎的事实并不一定反映在你身上,因为这些事情真的很难。(就像你一样,我也花了无数次的解析尝试才开始“得到”它。Haskell也是如此!)如果你真的觉得这个问题对你来说太难了,那么我强烈建议从更简单的工具开始(就像我上面的建议),这将有助于最终开发这些工具

相关问题