Introduction
J'utilise Scalaz 7'iteratees dans un certain nombre de projets, principalement pour le traitement de grandes-ish fichiers. Je voudrais commencer commutation de Scalaz ruisseaux, qui sont conçus pour remplacer l'iteratee paquet (qui, franchement, il manque beaucoup de pièces, et est une sorte de douleur à l'utilisation).
Les flux sont basés sur les machines (une autre variation sur le iteratee idée), qui ont également été mis en place en Haskell. J'ai utilisé le Haskell machines bibliothèque un peu, mais la relation entre les machines et les ruisseaux n'est pas tout à fait évident (pour moi, au moins), et la documentation pour les ruisseaux de la bibliothèque est encore un peu clairsemé.
Cette question est à propos d'une simple analyse de la tâche que j'aimerais voir mis en œuvre à l'aide de flux au lieu de iteratees. Je vais répondre à la question moi-même si personne ne me bat, mais je suis sûr que je ne suis pas le seul à faire (ou à tout le moins examiner) cette transition, et comme j'ai besoin de travailler à travers cet exercice, de toute façon, j'ai pensé que je pourrais aussi bien le faire en public.
Tâche
Censés j'ai un fichier contenant des phrases qui ont été segmentées et marqué avec les parties du discours:
no UH
, ,
it PRP
was VBD
n't RB
monday NNP
. .
the DT
equity NN
market NN
was VBD
illiquid JJ
. .
Il y a un jeton par ligne, des mots et des parties du discours sont séparés par un seul espace, et les lignes vides représentent les limites de la phrase. Je veux analyser ce fichier et retourne une liste de phrases, que l'on pourrait aussi bien représenter que des listes de n-uplets de chaînes de caractères:
List((no,UH), (,,,), (it,PRP), (was,VBD), (n't,RB), (monday,NNP), (.,.))
List((the,DT), (equity,NN), (market,NN), (was,VBD), (illiquid,JJ), (.,.)
Comme d'habitude, nous voulons à l'échec gracieusement, si l'on touche entrée non valide ou de la lecture du fichier d'exceptions, nous ne voulons pas avoir à s'inquiéter à propos de la fermeture de ressources manuellement, etc.
Un iteratee solution
Première de la lecture du fichier de trucs (qui devraient vraiment être une partie de l'iteratee paquet, qui actuellement ne fournit pas de quelque chose à distance de ce haut-niveau):
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()))
)
))
}
Et puis notre phrase lecteur:
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))
}
Et enfin, notre analyse de l'action:
val action =
I.consume[List[(String, String)], ErrorOr, List] %=
sentence.sequenceI &=
enumFile(new File("example.txt"))
On peut démontrer que cela fonctionne:
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), (.,.))
Et nous avons terminé.
Ce que je veux
Plus ou moins le même programme mis en œuvre à l'aide de Scalaz des ruisseaux au lieu de iteratees.