Итеративный ввод от Standard IO
Я использую скалаз 6.0 со скалой. Я использую итераторы для чтения из входного потока.
Вот простой файл с именем simple.txt, который у меня есть.
это
Является
Тест
Мой итератор создаст монаду io для печати строк
def printLines: IterV[String, IO[Unit]] = {
def step(currentIO: IO[Unit])(input: Input[String]): IterV[String, IO[Unit]] =
input match {
case El(x) => Cont(
step(
currentIO.flatMap(_ => putStrLn(x))
)
)
case IterV.Empty() => Cont(step(currentIO))
case EOF() => Done(currentIO, EOF[String])
}
Cont(step(io()))
}
Когда я использую enumeratorM
getFileLines(new File(".../simple.txt"))(printLines).flatMap(_.run).unsafePerformIO
Я получаю правильный вывод.
Когда я пытаюсь использовать
getLines(printLines).flatMap(_.run).unsafePerformIO
Я только получаю "Это" обратно на консоль. getLines использует стандартный поток ввода. Я добавил операторы отладки в iteratee, и getLines, похоже, отправляют EOF() после первой строки, и я не смог ее разрешить.
1 ответ
Это ошибка в определении getReaderLines
, Сравните текущую версию:
/** Enumerate the lines from a BufferedReader */
def getReaderLines(r: => BufferedReader): EnumeratorM[IO, String] =
new EnumeratorM[IO, String] {
def apply[A](it: IterV[String, A]) = {
def loop(i: IterV[String, A]): IO[IterV[String, A]] = i.fold(
done = (_,_) => io { i },
cont = k => for {
s <- rReadLn(r)
a <- s.map(l => loop(k(El(l)))).getOrElse(io(i))
} yield a
)
loop(it)
}
}
С тем, который работает:
/** Enumerate the lines from a BufferedReader */
def getReaderLines(r: => BufferedReader): EnumeratorM[IO, String] =
new EnumeratorM[IO, String] {
lazy val reader = r
def apply[A](it: IterV[String, A]) = {
def loop(i: IterV[String, A]): IO[IterV[String, A]] = i.fold(
done = (_,_) => io { i },
cont = k => for {
s <- rReadLn(reader)
a <- s.map(l => loop(k(El(l)))).getOrElse(io(i))
} yield a
)
loop(it)
}
}
Проблема в том, что r
является параметром по имени, что означает, что при условии, что getLines
определяется, текущая версия создает новый читатель, заключающий в себе стандартный ввод для каждого цикла.
Пока это не будет исправлено в библиотеке (и я сомневаюсь, что будет много спешки с выпуском 6.0.5 за дверь), самое простое решение - написать свой собственный. getLines
:
val getLines: EnumeratorM[IO, String] = {
val r = new BufferedReader(new InputStreamReader(System.in))
getReaderLines(r)
}
Это будет работать как ожидалось.