Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
通过Scala中的解析器线程化额外状态 我会给你tl;预先诊断_Scala_Haskell_Monads_Scalaz_Monad Transformers - Fatal编程技术网

通过Scala中的解析器线程化额外状态 我会给你tl;预先诊断

通过Scala中的解析器线程化额外状态 我会给你tl;预先诊断,scala,haskell,monads,scalaz,monad-transformers,Scala,Haskell,Monads,Scalaz,Monad Transformers,我试图使用state monad transformer在解析器中执行额外的状态,但是如果没有编写大量ma->tb版本的ma->mb方法,我就很难做任何有用的事情 一个示例解析问题 假设我有一个包含嵌套括号的字符串,括号内有数字: val input = "((617)((0)(32)))" 我还有一系列新的变量名(在本例中为字符): 我想从流的顶部提取一个名称,并将其分配给每个括号 表达式,然后将该名称映射到表示 括号的内容,嵌套的括号表达式(若有)替换为 名字 为了使这更具体,我希望上面的

我试图使用state monad transformer在解析器中执行额外的状态,但是如果没有编写大量
ma->tb
版本的
ma->mb
方法,我就很难做任何有用的事情

一个示例解析问题 假设我有一个包含嵌套括号的字符串,括号内有数字:

val input = "((617)((0)(32)))"
我还有一系列新的变量名(在本例中为字符):

我想从流的顶部提取一个名称,并将其分配给每个括号 表达式,然后将该名称映射到表示 括号的内容,嵌套的括号表达式(若有)替换为 名字

为了使这更具体,我希望上面的示例输入的输出是这样的:

val target = Map(
  'a' -> "617",
  'b' -> "0",
  'c' -> "32",
  'd' -> "bc",
  'e' -> "ad"
)
在给定的级别上,可能有一串数字或任意多个子表达式,但这两种内容不会混合在一个单括号表达式中

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

使用具有一点可变状态的解析器组合器 上面的示例是中解析问题的稍微简化版本 . 我与 大致如下所示的解决方案:

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版本差多少

但是我的
ExtraStateParsers
非常丑陋——我不想比我已经拥有的更多地考验你的耐心,所以我不会把它包括在这里(尽管,如果你真的想要的话)。我不得不为上面使用的每个
解析器
解析器
方法编写新版本
对于我的
extrastateparser
ESP
类型(
rep1
~>
),最通用的解决方案可能是重写Scala的解析器库,以适应解析时的一元计算(就像您部分做的那样),但这将是一项相当艰巨的任务

我建议使用的解决方案是,我们的每个结果不是
Parse[X]
类型的值,而是
Parse[State[Stream[Char],X]]
(别名为
ParserS[X]
)类型的值。因此,整个解析结果不是一个值,而是一个一元状态值,然后在一些
流[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': _*)));
  }
}
导入scala.util.parsing.combinator_
进口scalaz_
进口Scalaz_
导入遍历_
对象ParenParser使用状态扩展regexparser{
类型S[X]=状态[Stream[Char],X];
类型解析器[X]=解析器[S[X]];
//哈斯凯尔的各州“回归”
def-toState[S,X](X:X):状态[S,X]=get(=>X)
//哈斯凯尔的《国家地图》
def mapM[S,X](l:List[State[S,X]]):State[S,List[X]]=
l、 遍历[({typel[Y]=状态[S,Y]})#l,X](恒等式);
// .................................................
//从状态内的流中读取下一个字符
//并将状态更新到流的尾部。
def next:S[Char]=状态(S=>(S.tail,S.head));
定义参数:解析器[列表[(字符,字符串)]]=
(“~>内容下一个映射(v=>(v->s)::m)
})
定义内容:解析器[(字符串,列表[(字符,字符串)]]]=数字|参数;
定义数字:解析器[(字符串,列表[(字符,字符串)])]=
“\\d+”.r^^^(->Nil)^^(toState)
定义参数:解析器[(字符串,列表[(字符,字符串)])]=
代表1(公园)^^(地图)(
ps=>ps.map(u.head._1).mkString->ps.flatten
))
def parse(s:String):ParseResult[s[Map[Char,String]]=
parseAll(paren,s).map(0.map(0.toMap))
def parse(s:String,name:Stream[Char]):ParseResult[Map[Char,String]]=
解析.map(!名称);
}
对象ParenParserTest扩展应用程序{
{
println(ParenParser.parse(((617)((0)(32))),Stream('a'到'z':*);
}
}


注意:我认为您使用的
StateT[Parser,Stream[Char],
的方法在概念上是不正确的。该类型表示我们正在构造给定某种状态(名称流)的解析器。因此,有可能给定不同的流,我们得到不同的解析器。这不是我们想要做的。我们只希望解析的结果取决于名称,而不是整个解析器。这样,
parser[State[Stream[Char],]]
似乎更合适(Haskell的Parsec采用了类似的方法,状态/单子在解析器内部)。

我认为具有可变状态的
迭代器
解决方案是最“惯用”的Scala解决方案。@DanBurton:我同意,但避免这种可变状态可能是一种有趣的(有时是有用的)在本例中,有一种明确适用的不可变方法——我就是不知道如何干净地使用它。如果您将解析器实现为monad transformer(名为ParserT?),它是可行的然后为ParserT实现一个MonadState实例,如果它的内部monad是MonadState的实例。@Yoweight:我考虑过这种方法,但它似乎更复杂,因为我使用的是标准库的
解析器
。不过我会研究一下,谢谢。谢谢!这非常有用。关于我原始公式中的最后一点关于问题n,如果流没有名称,我希望解析器失败,这会影响堆栈的形状。我将再次尝试您的方法,谢谢。
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)))"
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)
}
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': _*)));
  }
}