最小纯应用解析器

2024-02-07

我试图找出如何基于一个简单的构建“纯应用解析器”parser http://dev.stephendiehl.com/fun/002_parsers.html执行。解析器在其实现中不会使用 monad。我之前问过这个问题,但错误地回答了这个问题,所以我再试一次。

这是基本类型及其Functor, Applicative and Alternative实施:

newtype Parser a = Parser { parse :: String -> [(a,String)] }

instance Functor Parser where
  fmap f (Parser cs) = Parser (\s -> [(f a, b) | (a, b) <- cs s])

instance Applicative Parser where
  pure = Parser (\s -> [(a,s)])
  (Parser cs1) <*> (Parser cs2) = Parser (\s -> [(f a, s2) | (f, s1) <- cs1 s, (a, s2) <- cs2 s1])

instance Alternative Parser where
  empty = Parser $ \s -> []
  p <|> q = Parser $ \s ->
    case parse p s of
      [] -> parse q s
      r  -> r

The item函数从流中取出一个字符:

item :: Parser Char
item = Parser $ \s ->
  case s of
   [] -> []
   (c:cs) -> [(c,cs)]

此时我想实现digit。我当然可以这样做:

digit = Parser $ \s ->
  case s of
    [] -> []
    (c:cs) -> if isDigit c then [(c, cs)] else []

但我正在复制代码item。我想实施digit基于item.

我该如何实施digit, using item从流中取出一个字符,然后检查该字符是否是数字,而不将一元概念引入实现中?


首先,让我们写下我们目前手头上的所有工具:

-- Data constructor
Parser :: (String -> [(a, String)]) -> Parser a

-- field accessor
parse :: Parser a -> String -> [(a, String)]

-- instances, replace 'f' by 'Parser'
fmap  :: Functor f     =>   (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
pure  :: Applicative f =>                 a -> f a

-- the parser at hand
item :: Parser Char

-- the parser we want to write with item
digit :: Parser Char
digit = magic item

-- ?
magic :: Parser Char -> Parser Char

当前真正的问题是“什么是magic“?我们能用的东西就这么多。它的类型表明fmap,但我们可以排除这种可能性。我们所能提供的只是一些功能a -> b,但是没有f :: Char -> Char这使得fmap f表明失败。

关于什么(<*>),这有帮助吗?好吧,再说一遍,答案是否定的。我们在这里唯一能做的就是采取(a -> b)脱离上下文并应用它;无论这在给定的上下文中意味着什么Applicative。我们可以统治pure out.

问题是我们需要检查Char that item可能会解析and改变上下文。我们需要类似的东西Char -> Parser Char

但我们没有统治Parser or parse out!

magic p = Parser $ \s ->
  case parse p s of -- < item will be used here
    [(c, cs)] -> if isDigit c then [(c, cs)] else []
    _         -> []

是的,我知道,这是重复的代码,但现在它正在使用item。它正在使用item在检查角色之前。这是我们可以使用的唯一方法item这里。现在,隐含着某种顺序:item必须先成功digit可以做它的工作。

或者,我们可以尝试这样:

digit' c :: Char -> Parser Char
digit' c = if isDigit c then pure c else empty

但是之后fmap digit' item会有这样的类型Parser (Parser Char),它只能通过类似 join 的函数折叠。这就是为什么monad 比 applicative 更强大 https://stackoverflow.com/q/34932624/1139697.

话虽这么说,如果您首先使用更通用的函数,您可以绕过所有 monad 要求:

satisfy :: (Char -> Bool) -> Parser Char
satisfy = Parser $ \s -> 
   case s of
     (c:cs) | p c -> [(c, cs)]
     _            -> []

然后您可以定义两者item and digit按照satisfy:

item  = satisfy (const True)
digit = satisfy isDigit

那样digit不必检查先前解析器的结果。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

最小纯应用解析器 的相关文章

  • “Eta减少”并不总是在Haskell中举行?

    我发现我可以说 LANGUAGE RankNTypes f1 forall b b gt b gt forall c c gt c f1 f id f HLint 告诉我我可以在这里做 Eta 减少 但是 f2 forall b b gt
  • 如何在 Haskell 中漂亮地打印表格?

    我想在 Haskell 中漂亮地打印一个类似表格的数据结构 列列表 例如 Table StrCol strings a bc c IntCol ints 1 30 2 DblCol doubles 2 0 4 5 3 2 应该渲染类似 st
  • 将人类日期(当地时间 GMT)转​​换为日期

    我正在服务器上工作 服务器正在向我发送 GMT 本地日期的日期 例如Fri Jun 22 09 29 29 NPT 2018在字符串格式上 我将其转换为日期 如下所示 SimpleDateFormat simpleDateFormat ne
  • Haskell 入门

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 几天来 我一直试图理解 Haskell 中的函数式编程范例 我通过阅读教程和观看截屏视频
  • 在Python中连续解析文件

    我正在编写一个脚本 该脚本使用 HTTP 流量行解析文件 并取出域 目前仅将它们打印到屏幕上 我正在使用 httpry 将流量连续写入文件 这是我用来删除域名的脚本 usr bin python import re input open r
  • 无论如何要抓取重定向的链接吗?

    无论如何 我可以让 python 单击一个链接 例如 bit ly 链接 然后抓取生成的链接吗 当我抓取某个页面时 我唯一可以抓取的链接是重定向的链接 它重定向到的位置就是我需要的信息所在的位置 重定向有 3 种类型 HTTP 作为响应标头
  • PHP解析xml文件错误

    我正在尝试使用 simpleXML 来获取数据http rates fxcm com RatesXML http rates fxcm com RatesXML Using simplexml load file 我有时会遇到错误 因为这个
  • 找不到模块“Yesod”

    我有以下代码 LANGUAGE TypeFamilies QuasiQuotes MultiParamTypeClasses TemplateHaskell OverloadedStrings module Simple where imp
  • 这个对自身单位的列表理解是如何工作的?

    在 haskell IRC 频道中有人问 是否有一种简洁的方法来定义一个列表 其中第 n 个条目是之前所有条目的平方和 我认为这听起来像一个有趣的谜题 递归定义无限列表是我真正需要练习的事情之一 所以我启动了 GHCi 并开始尝试递归定义
  • 如何使用 BeautifulSoup 从表中选择特定行?

    So I have a question related to a previous question but I realized I needed to go one level more to get an 11 digit NDC
  • Haskell 输入返回元组

    我想知道 IO 函数是否可以返回元组 因为我想从这个函数中获取这些元组作为另一个函数的输入 investinput IO gt Char Int investinput do putStrLn Enter Username username
  • 在 monad 转换器类型类中使用列表 monad?

    我的目标是创建一个在 ReaderT WriterT 堆栈或 RWS 堆栈中使用列表 monad 的函数 更一般地说 如何在 mtl 类型类 例如 MonadReader MonadWriter 中使用列表 monad 我为什么要尝试这样做
  • 在 JAVA 中使用 SAX 解析器从 XML 文件中提取文本节点

    因此 我目前正在使用 SAX 尝试从我正在处理的大量 xml 文档中提取一些信息 到目前为止 提取属性值确实很容易 但是 我不知道如何从文本节点中提取实际值 例如 在给定的 XML 文档中
  • 在 Haskell 中合并两个列表

    无法弄清楚如何合并两个列表通过以下方式在哈斯克尔 INPUT 1 2 3 4 5 11 12 13 14 OUTPUT 1 11 2 12 3 13 4 14 5 我想提出一个更懒的合并版本 merge ys ys merge x xs y
  • 如何通过“cabal build”或“stack build”构建带有图标的项目

    我想构建一个带有图标的可执行文件 通过谷歌搜索 我发现这里的说明 https wiki haskell org Setting an executable icon 但它只能通过编译源文件来工作ghc 如果我想构建一个具有可执行文件的项目c
  • 不同编程语言中的浮点数学

    我知道浮点数学充其量可能是丑陋的 但我想知道是否有人可以解释以下怪癖 在大多数编程语言中 我测试了 0 4 到 0 2 的加法会产生轻微的错误 而 0 4 0 1 0 1 则不会产生错误 两者计算不平等的原因是什么 在各自的编程语言中可以采
  • Haskell Data.Decimal 作为 Aeson 类型

    是否可以解析一个数据 十进制 https hackage haskell org package Decimal 0 4 2 docs Data Decimal html使用 Aeson 包从 JSON 获取 假设我有以下 JSON foo
  • 如何展平解析树并存储在字符串中以进行进一步的字符串操作 python nltk

    我正在尝试从树结构中获取扁平树 如下所示 我想将整个树放在一个字符串中 就像没有检测到坏树错误一样 S NP SBJ NP DT The JJ high JJ seven day PP IN of NP DT the CD 400 NNS
  • 我是否需要采取明确的操作来促进与持久数据结构的共享?

    我来自命令式背景 正在尝试实现一个简单的不相交集 并集查找 数据结构 以获得在 Haskell 中创建和修改 持久 数据结构的一些练习 目标是有一个简单的实现 但我也关心效率 我的问题与此相关 首先 我创建了一个按等级并集的不相交集森林实现
  • 关于“没有绑定的类型签名”的错误

    我在 Haskell 中遇到 ASCII 问题 fromEnum Char gt Int toEnum Int gt Char offset Int offset fromEnum A fromEnum a toUpper Char gt

随机推荐