通过 Scala 中的解析器线程化额外状态

2024-01-04

我先给你讲讲 tl;dr

我正在尝试使用状态单子变压器Scalaz 7 https://github.com/scalaz/scalaz通过解析器线程化额外的状态,如果不编写一个,我就很难做任何有用的事情lot of t m a -> t m b的版本m a -> m b方法。

解析问题示例

假设我有一个包含嵌套括号且其中包含数字的字符串:

val input = "((617)((0)(32)))"

我还有一串新的变量名称(在本例中为字符):

val names = Stream('a' to 'z': _*)

我想从流的顶部提取一个名称并将其分配给每个括号 解析它时的表达式,然后将该名称映射到表示 括号的内容,其中嵌套的括号表达式(如果有)替换为它们的 名称。

为了使这一点更加具体,我希望上面的示例输入的输出如下所示:

val target = Map(
  'a' -> "617",
  'b' -> "0",
  'c' -> "32",
  'd' -> "bc",
  'e' -> "ad"
)

在给定级别上可能存在一串数字或任意多个子表达式,但这两种内容不会混合在单个括号表达式中。

为了简单起见,我们假设名称流永远不会 包含重复项或数字,并且它将始终包含足够的 我们输入的名称。

使用带有一些可变状态的解析器组合器

上面的例子是解析问题的稍微简化的版本这个堆栈溢出问题 https://stackoverflow.com/q/12442615/334519. I 回答了这个问题 https://stackoverflow.com/a/12443270/334519和 解决方案大致如下:

import scala.util.parsing.combinator._

class ParenParser(names: Iterator[Char]) extends RegexParsers {
  def paren: Parser[List[(Char, String)]] = "(" ~> contents <~ ")" ^^ {
    case (s, m) => (names.next -> s) :: m
  }

  def contents: Parser[(String, List[(Char, String)])] = 
    "\\d+".r ^^ (_ -> Nil) | rep1(paren) ^^ (
      ps => ps.map(_.head._1).mkString -> ps.flatten
    )

  def parse(s: String) = parseAll(paren, s).map(_.toMap)
}

这还不错,但我更愿意避免可变状态。

我想要的是

哈斯克尔的Parsec http://hackage.haskell.org/package/parsec-3.1.3图书馆制作 将用户状态添加到解析器非常简单:

import Control.Applicative ((*>), (<$>), (<*))
import Data.Map (fromList)
import Text.Parsec

paren = do
  (s, m) <- char '(' *> contents <* char ')'
  h : t  <- getState
  putState t
  return $ (h, s) : m
  where
    contents
      =  flip (,) []
     <$> many1 digit
     <|> (\ps -> (map (fst . head) ps, concat ps))
     <$> many1 paren

main = print $
  runParser (fromList <$> paren) ['a'..'z'] "example" "((617)((0)(32)))"

这是我上面的 Scala 解析器的相当简单的翻译,但没有可变状态。

我尝试过的

我试图使用 Scalaz 的状态单子转换器尽可能接近秒差距解决方案,所以而不是Parser[A]我正在与StateT[Parser, Stream[Char], A]。 我有一个“解决方案”,允许我编写以下内容:

import scala.util.parsing.combinator._
import scalaz._, Scalaz._

object ParenParser extends ExtraStateParsers[Stream[Char]] with RegexParsers {
  protected implicit def monadInstance = parserMonad(this)

  def paren: ESP[List[(Char, String)]] = 
    (lift("(" ) ~> contents <~ lift(")")).flatMap {
      case (s, m) => get.flatMap(
        names => put(names.tail).map(_ => (names.head -> s) :: m)
      )
    }

  def contents: ESP[(String, List[(Char, String)])] =
    lift("\\d+".r ^^ (_ -> Nil)) | rep1(paren).map(
      ps => ps.map(_.head._1).mkString -> ps.flatten
    )

  def parse(s: String, names: Stream[Char]) =
    parseAll(paren.eval(names), s).map(_.toMap)
}

这是可行的,而且它并不比可变状态版本或秒差距版本简洁多少。

But my ExtraStateParsers丑陋如罪——我不想再考验你的耐心了,所以我不会把它包括在这里(尽管这是一个链接 https://gist.github.com/3747234,如果你真的想要的话)。我不得不为每一个都编写新版本Parser and Parsers我上面使用的方法 为了我的ExtraStateParsers and ESP types (rep1, ~>, <~, and |,以防你在数)。如果我需要使用其他组合器,我还必须编写它们的新状态转换器级版本。

有没有更干净的方法来做到这一点?我很想看到 Scalaz 7 的状态 monad 转换器用于通过解析器对状态进行线程化的示例,但 Scalaz 6 或 Haskell 的示例也很有用且值得赞赏。


也许最通用的解决方案是重写 Scala 的解析器库以在解析时适应单子计算(就像您部分所做的那样),但这将是一项相当费力的任务。

我建议使用一个解决方案ScalaZ http://scalaz.github.com/scalaz/'s State http://scalaz.github.com/scalaz/scalaz-2.9.1-6.0.4/doc/index.html#scalaz.State其中我们的每个结果都不是类型的值Parse[X],但是类型的值Parse[State[Stream[Char],X]](别名为ParserS[X])。因此,整体解析结果不是一个值,而是一个单子状态值,然后在某些Stream[Char]。这几乎是一个 monad 变压器,但我们必须手动进行提升/卸载。它使代码有点难看,因为我们有时需要提升值或使用map/flatMap虽然有很多地方,但我认为还是有道理的。

import scala.util.parsing.combinator._
import scalaz._
import Scalaz._
import Traverse._

object ParenParser extends RegexParsers with States {
  type S[X] = State[Stream[Char],X];
  type ParserS[X] = Parser[S[X]];


  // Haskell's `return` for States
  def toState[S,X](x: X): State[S,X] = gets(_ => x)

  // Haskell's `mapM` for State
  def mapM[S,X](l: List[State[S,X]]): State[S,List[X]] =
    l.traverse[({type L[Y] = State[S,Y]})#L,X](identity _);

  // .................................................

  // Read the next character from the stream inside the state
  // and update the state to the stream's tail.
  def next: S[Char] = state(s => (s.tail, s.head));


  def paren: ParserS[List[(Char, String)]] =
    "(" ~> contents <~ ")" ^^ (_ flatMap {
      case (s, m) => next map (v => (v -> s) :: m)
    })


  def contents: ParserS[(String, List[(Char, String)])] = digits | parens;
  def digits: ParserS[(String, List[(Char, String)])] =
    "\\d+".r ^^ (_ -> Nil) ^^ (toState _)
  def parens: ParserS[(String, List[(Char, String)])] =
    rep1(paren) ^^ (mapM _) ^^ (_.map(
        ps => ps.map(_.head._1).mkString -> ps.flatten
      ))


  def parse(s: String): ParseResult[S[Map[Char,String]]] =
    parseAll(paren, s).map(_.map(_.toMap))

  def parse(s: String, names: Stream[Char]): ParseResult[Map[Char,String]] =
    parse(s).map(_ ! names);
}

object ParenParserTest extends App {
  {
    println(ParenParser.parse("((617)((0)(32)))", Stream('a' to 'z': _*)));
  }
}

Note:我相信你的方法StateT[Parser, Stream[Char], _]在概念上不正确。该类型表示我们正在构造一个给定某种状态(名称流)的解析器。因此,给定不同的流,我们可能会得到不同的解析器。这不是我们想要做的。我们只希望result解析取决于名称,而不是整个解析器。这样Parser[State[Stream[Char],_]]似乎更合适(Haskell 的 Parsec 采用类似的方法,状态/单子位于解析器内部)。

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

通过 Scala 中的解析器线程化额外状态 的相关文章

  • 为什么自类型类可以声明类

    我知道 Scala 只能混合特征 这对于依赖注入和蛋糕模式是有意义的 我的问题是为什么我仍然可以声明一个需要另一个 类 但不需要特征的类 Code class C class D self C gt 这仍然编译成功 我认为它应该编译失败 因
  • Spark 2.2 无法将 df 写入 parquet

    我正在构建一个聚类算法 我需要存储模型以供将来加载 我有一个具有以下架构的数据框 val schema new StructType add StructField uniqueId LongType add StructField tim
  • Scala 使用的 Redis 客户端库建议

    我正在计划使用 Scala 中的 Redis 实例进行一些工作 并正在寻找有关使用哪些客户端库的建议 理想情况下 如果存在一个好的库 我希望有一个为 Scala 而不是 Java 设计的库 但如果现在这是更好的方法 那么仅使用 Java 客
  • 如何将 Java 地图转换为在 Scala 中使用?

    我正在开发一个 Scala 程序 该程序调用 Java 库中的函数 处理结果并生成 CSV 有问题的 Java 函数如下所示 Map
  • Kafka 分区键无法正常工作

    我正在努力解决如何正确使用分区键机制的问题 我的逻辑是设置分区号为3 然后创建三个分区键为 0 1 2 然后使用分区键创建三个KeyedMessage 例如 KeyedMessage 主题 0 消息 KeyedMessage 主题 1 消息
  • Java 拥有闭包后 Scala 的优势 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 随着 Java 中添加了闭包 作为语言选择 Scala 相对于 Java 的优势是什么 有人可以详细说明一下有什么优点吗 除了闭包 J
  • scala 返回列表中的第一个 Some

    我有一个清单l List T1 目前我正在执行以下操作 myfun T1 gt Option T2 val x Option T2 l map myfun l flatten find gt true The myfun函数返回 None
  • 如何在 Lift 框架中添加新页面

    如何在 lift 中的 webapp 目录中添加一个可供用户访问的新页面 目前只能通过index html访问http localhost 8080 com http localhost 8080 or http localhost 808
  • 如何通过“cabal build”或“stack build”构建带有图标的项目

    我想构建一个带有图标的可执行文件 通过谷歌搜索 我发现这里的说明 https wiki haskell org Setting an executable icon 但它只能通过编译源文件来工作ghc 如果我想构建一个具有可执行文件的项目c
  • Data.Sequence 中的 inits 和 tails 如何工作?

    Louis Wasserman 编写了当前的实现inits and tails in Data Sequence 他表示它们非常高效 事实上 只要查看代码 我就可以看到 无论它们在做什么 它们都是以干净 自上而下的方式进行的 这往往会给惰性
  • 了解 Spark 中的 DAG

    问题是我有以下 DAG 我认为当需要洗牌时 火花将工作划分为不同的阶段 考虑阶段 0 和阶段 1 有些操作不需要洗牌 那么为什么 Spark 将它们分成不同的阶段呢 我认为跨分区的实际数据移动应该发生在第 2 阶段 因为这里我们需要cogr
  • Spark:查找前 n 个值的高性能方法

    我有一个很大的数据集 我想找到具有 n 个最高值的行 id count id1 10 id2 15 id3 5 我能想到的唯一方法是使用row number没有分区就像 val window Window orderBy desc coun
  • Haskell:不在范围内:数据构造函数

    今天开始在学校学习 haskell 我遇到了函数问题 我不明白为什么它不在范围内 代码如下 ff Char gt Char gt Char ff A B x 0 y 1 x lt A y lt B x 1 y 0 和错误 md31 hs 2
  • 用于共享大型不可变对象的工厂/缓存策略

    我的问题很像上一篇文章最佳哈希集初始化 Scala Java https stackoverflow com questions 14714900 optimal hashset initialization scala java 我想用的
  • Haskell Data.Decimal 作为 Aeson 类型

    是否可以解析一个数据 十进制 https hackage haskell org package Decimal 0 4 2 docs Data Decimal html使用 Aeson 包从 JSON 获取 假设我有以下 JSON foo
  • sbt 找不到启动器 jar:./bin/sbt-launch.jar

    我已经成功地使用 sbt 进行基本的 Scala 编译好几年了 但有一天它由于某种原因停止工作 我在自己的帐户中使用它 但我决定让我的系统管理员在我们的本地网络上安装最新版本 当我尝试运行它时 我得到 找不到启动器 jar bin sbt
  • 如何在Haskell中实现词法分析器和解析器

    我在这里得到了这段代码 它是用Haskell结构的命令式编程语言编写的程序 所以问题是 我如何为这种语言实现词法分析器和解析器 该程序被定义为一系列语句有 6 种类型 goto write stop if goto 和 int int n
  • InvalidRequestException(为什么:empid 如果包含 Equal,则不能被多个关系限制)

    这是关于我从 Apache Spark 查询 Cassandra 时遇到的问题 Spark 的正常查询工作正常 没有任何问题 但是当我使用关键条件进行查询时 出现以下错误 最初 我尝试查询复合键列族 它也给出了与下面相同的问题 由以下原因引
  • Python 相当于 Scala 案例类

    Python 中是否有与 Scala 的 Case Class 等效的东西 就像自动生成分配给字段而无需编写样板的构造函数一样 当前执行此操作的现代方法 从 Python 3 7 开始 是使用数据类 https www python org
  • 你能在 scala 中使用 varargs 柯里化一个函数吗?

    我正在考虑如何用可变参数柯里化一种方法 然后我意识到我什至不知道如何去做 理想情况下 它应该让您可以随时开始使用它 然后以可迭代结束 def concat strs String strs mkString val curriedConca

随机推荐

  • 具有多个数据库的 Rails RSpec

    我运行一个 Rails 应用程序 我们正在将注册流程拆分为一个单独的应用程序 注册应用程序有自己独立的数据库 用于 CMS 和收集潜在客户 但它还需要访问主数据库 使用这个效果非常好ActiveRecord Base establish c
  • gob 恐慌解码接口

    我有一个带有未导出字段的结构 应该对其进行 gob 编码和解码 Say type A struct s int func a A Inc a s 显然在这种情况下我需要实施gob GobEncoder and gob GobDecoder接
  • 动画 gif 无法播放 - 鼠标侦听器 - 鼠标输入事件

    我有一个按钮 我已将其替换为图像 悬停时我希望该图像播放动画 gif 我添加了一个鼠标监听器并输入了将图像更改为 gif 的代码 图像变为gif 然而 gif 并没有动画 我在这个网站上查找过以前的答案 很少但没有一个能够提供帮助 Over
  • 如何在 VS Code 中禁用 C++ 错误检查?

    我正在生成 C 示例代码文件 因此它们有很多固有错误 例如虚构的函数名称 我正在尝试删除文件中的所有错误消息和与错误相关的语法突出显示 我想 C 有一些设置 但我找不到它 打开命令面板 CTRL SHIFT P gt C Cpp 切换错误曲
  • 有没有办法在MonoTouch中制作原生的cocoa类库?

    我知道 MonoTouch 将代码编译为本机机器代码 有没有办法使用 MonoTouch 制作一个本机 a 类库 需要明确的是 我不想在两者之间共享代码 因为我知道这是不可能的 我相信这是可能的 但这将是一项艰苦的工作 而且离收缩包装的开箱
  • FragmentStatePagerAdapter 从 API 27 开始已弃用

    FragmentStatePagerAdapter从 API 27 开始已弃用 替代方案是什么FragmentStatePagerAdapter private class MainPagerAdapter extends Fragment
  • “在上下文或道具中找不到“store””我在尝试简单的react-redux代码时收到此错误

    这是我的书单容器 import React Component from react import connect from react redux class BookList extends Component renderList r
  • 在哪里放置 Windows 窗体项目的中央错误处理程序

    在 ASP NET 中我可以使用Application Error在 global asax 中 以便处理任何未处理的错误 Windows 窗体中有等效的吗 是的 它的 AppDomain UnhandledException using
  • 将 Web 服务添加到已有的 Java 项目中

    我是 Java 新手 我有一个Java项目 它在我的 Windows 7 机器上完美运行 我想将该项目的一些功能用作 Web 服务 以便能够在我的 Silverlight 应用程序中使用它们 Silverlight 应用程序和这个 Java
  • Swift 从 NSDictionary 读取数据

    我正在使用这段代码来读取数据NSDictionary let itemsArray NSArray response objectForKey items as NSArray let nextPageToken String respon
  • 如何对多列进行逆透视 SQL Server

    在我的应用程序中 我使用了商店产品描述值 如下所示 ID BILLNO CUS NAME DATE TOT BAL S1 S2 S3 S4 D1 D2 D3 D4 Q1 Q2 Q3 Q4 U1 U2 U3 U4 T1 T2 T3 T4 TO
  • DFT 频率组件 Opencv

    我使用以下链接来了解如何在Opencv中使用DFT http docs opencv org doc tutorials core discrete fourier transform discrete fourier transform
  • Node Express 发送图像文件作为 API 响应

    我用谷歌搜索了这个但找不到答案 但这一定是一个常见问题 这是同样的问题节点请求 读取图像流 通过管道返回响应 https stackoverflow com questions 17004990 node request read imag
  • Python“if X == Y and Z”语法

    做这个 if key name and item 意思和这个一样 if key name and if key item 如果是这样 我完全困惑了深入 Python 中的示例 5 14 http diveintopython net obj
  • pandas 数据透视表 - 有序类别导致意外的边距

    使用 python 3 7 和 pandas 0 23 4 我正在尝试使用有序分类数据制作数据透视表 如果我包括边距 则小计的顺序似乎不正确 import pandas as pd m male f female data num 0 1
  • 如何在 Xcode 4 中使用 nib 创建自定义 UITableViewCell?

    在 Xcode 3 中 我可以选择在创建表视图单元格子类时创建笔尖 在 Xcode 4 中 它只生成 h m 文件 如何使用 xib 文件创建子类 编辑 参见下面的屏幕截图 EDIT 此外 是否有任何方法可以自动创建 UITableView
  • MySQL 嵌套 JSON 列搜索并提取子 JSON

    我有一个 MySQL 表authors有柱子id name and published books 在这之中 published books是一个 JSON 列 有了样本数据 id name published books 1 Tina 1
  • keydown 事件中的输入验证

    我正在尝试在以下过程中对用户文本输入进行信息验证keydown事件 我试图验证的原因是keydown事件是因为我不想显示那些被认为是非法的字符input开头的盒子 我写的验证是这样的 function validateUserInput v
  • 用于显式实例化一组固定类型的单行代码

    我有一组固定的四种类型A B C and D 以及大量使用这些类型的类和函数模板 为了减少编译时间 我想将这些模板的定义放入 cpp 文件中 并显式实例化这组固定类型的模板 然而 显式实例化引入了很多样板代码 我想减少这些代码 是否有一种优
  • 通过 Scala 中的解析器线程化额外状态

    我先给你讲讲 tl dr 我正在尝试使用状态单子变压器Scalaz 7 https github com scalaz scalaz通过解析器线程化额外的状态 如果不编写一个 我就很难做任何有用的事情lot of t m a gt t m