gpt4 book ai didi

scala - 使用 Scalaz Stream 进行解析任务(替换 Scalaz Iteratees)

转载 作者:行者123 更新时间:2023-12-03 01:09:21 27 4
gpt4 key购买 nike

简介

我使用Scalaz 7在许多项目中进行迭代,主要用于处理大型文件。我想开始切换到 Scalaz streams ,它们旨在替换 iteratee 包(坦率地说,它缺少很多部分,并且使用起来有点痛苦)。

流基于 machines (迭代思想的另一个变体),其中有 also been implemented在 haskell 。我使用过一点 Haskell 机器库,但是机器和流之间的关系并不完全明显(至少对我来说),流库的文档是 still a little sparse .

这个问题是关于一个简单的解析任务,我希望看到它使用流而不是迭代器来实现。如果没有人打败我,我会自己回答这个问题,但我确信我不是唯一一个正在进行(或至少考虑)这一转变的人,而且由于无论如何我都需要完成这项练习,所以我我想我不妨在公共(public)场合这样做。

任务

假设我有一个文件,其中包含已标记化并标有词性的句子:

no UH
, ,
it PRP
was VBD
n't RB
monday NNP
. .

the DT
equity NN
market NN
was VBD
illiquid JJ
. .

每行有一个标记,单词和词性由一个空格分隔,空行代表句子边界。我想解析这个文件并返回一个句子列表,我们也可以将其表示为字符串元组列表:

List((no,UH), (,,,), (it,PRP), (was,VBD), (n't,RB), (monday,NNP), (.,.))
List((the,DT), (equity,NN), (market,NN), (was,VBD), (illiquid,JJ), (.,.)

像往常一样,如果我们遇到无效输入或文件读取异常,我们希望优雅地失败,我们不想担心手动关闭资源等。

迭代解决方案

首先是一些一般的文件读取内容(这确实应该是 iteratee 包的一部分,目前它不提供任何远程的高级功能):

import java.io.{ BufferedReader, File, FileReader }
import scalaz._, Scalaz._, effect.IO
import iteratee.{ Iteratee => I, _ }

type ErrorOr[A] = EitherT[IO, Throwable, A]

def tryIO[A, B](action: IO[B]) = I.iterateeT[A, ErrorOr, B](
EitherT(action.catchLeft).map(I.sdone(_, I.emptyInput))
)

def enumBuffered(r: => BufferedReader) = new EnumeratorT[String, ErrorOr] {
lazy val reader = r
def apply[A] = (s: StepT[String, ErrorOr, A]) => s.mapCont(k =>
tryIO(IO(Option(reader.readLine))).flatMap {
case None => s.pointI
case Some(line) => k(I.elInput(line)) >>== apply[A]
}
)
}

def enumFile(f: File) = new EnumeratorT[String, ErrorOr] {
def apply[A] = (s: StepT[String, ErrorOr, A]) => tryIO(
IO(new BufferedReader(new FileReader(f)))
).flatMap(reader => I.iterateeT[String, ErrorOr, A](
EitherT(
enumBuffered(reader).apply(s).value.run.ensuring(IO(reader.close()))
)
))
}

然后我们的句子阅读器:

def sentence: IterateeT[String, ErrorOr, List[(String, String)]] = {
import I._

def loop(acc: List[(String, String)])(s: Input[String]):
IterateeT[String, ErrorOr, List[(String, String)]] = s(
el = _.trim.split(" ") match {
case Array(form, pos) => cont(loop(acc :+ (form, pos)))
case Array("") => cont(done(acc, _))
case pieces =>
val throwable: Throwable = new Exception(
"Invalid line: %s!".format(pieces.mkString(" "))
)

val error: ErrorOr[List[(String, String)]] = EitherT.left(
throwable.point[IO]
)

IterateeT.IterateeTMonadTrans[String].liftM(error)
},
empty = cont(loop(acc)),
eof = done(acc, eofInput)
)
cont(loop(Nil))
}

最后是我们的解析操作:

val action =
I.consume[List[(String, String)], ErrorOr, List] %=
sentence.sequenceI &=
enumFile(new File("example.txt"))

我们可以证明它是有效的:

scala> action.run.run.unsafePerformIO().foreach(_.foreach(println))
List((no,UH), (,,,), (it,PRP), (was,VBD), (n't,RB), (monday,NNP), (.,.))
List((the,DT), (equity,NN), (market,NN), (was,VBD), (illiquid,JJ), (.,.))

我们就完成了。

我想要什么

或多或少使用 Scalaz 流而不是迭代器实现相同的程序。

最佳答案

scalaz-stream 解决方案:

import scalaz.std.vector._
import scalaz.syntax.traverse._
import scalaz.std.string._

val action = linesR("example.txt").map(_.trim).
splitOn("").flatMap(_.traverseU { s => s.split(" ") match {
case Array(form, pos) => emit(form -> pos)
case _ => fail(new Exception(s"Invalid input $s"))
}})

我们可以证明它是有效的:

scala> action.collect.attempt.run.foreach(_.foreach(println))
Vector((no,UH), (,,,), (it,PRP), (was,VBD), (n't,RB), (monday,NNP), (.,.))
Vector((the,DT), (equity,NN), (market,NN), (was,VBD), (illiquid,JJ), (.,.))

我们就完成了。

traverseU 函数是一个常见的 Scalaz 组合器。在本例中,它用于在 Process 单子(monad)中遍历由 splitOn 生成的句子 Vector。它相当于 map 后跟 sequence

关于scala - 使用 Scalaz Stream 进行解析任务(替换 Scalaz Iteratees),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18112224/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com