Play Enumeratee, который подсчитывает и выбирает значения из входного Enumerator

Меня интересует, как лучше всего реализовать что-то подобное с библиотекой Play Iteratee:

def sampleEvery[A](i: Int): Enumeratee[A, Int] = ???

таким образом, что при наличии потока A Enumeratee будет их подсчитывать и выдавать текущее значение для этого счетчика каждые i, а затем также добавлять последнее значение (если возможно, без повторения значения).

Например, Enumerator('q','w','e','r','t','y','u','i','o').through(sampleEvery(3)) даст что-то вроде Enumerator(0,3,6,8).


person betehess    schedule 03.08.2015    source источник


Ответы (1)


Вы ищете перечислитель для хранения состояния текущего количества уже просмотренных элементов, для чего они на самом деле не предназначены. Вы можете обойти это, закрыв вокруг var counter: Int, но вы можете (и должны) подумать, что это уродливо. В любом случае, ниже приведен пример, который, как мне кажется, имеет поведение, которое вы ищете, используя scala repl.

Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :paste
// Entering paste mode (ctrl-D to finish)

import play.api.libs.iteratee._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._

def sampleEvery[A](i: Int): Enumeratee[A, Int] = {
  var counter: Int = 0
  Enumeratee.grouped[A](Iteratee.takeUpTo[A](i)) compose Enumeratee.map[Seq[A]]{seq => counter += seq.length; counter}
}

val e = Enumerator('q','w','e','r','t','y','u','i','o')
val promise = e.through(sampleEvery(3)).run(Iteratee.getChunks[Int])
Await.result(promise, 200 milliseconds)

// Exiting paste mode, now interpreting.

import play.api.libs.iteratee._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
sampleEvery: [A](i: Int)play.api.libs.iteratee.Enumeratee[A,Int]
e: play.api.libs.iteratee.Enumerator[Char] = play.api.libs.iteratee.Enumerator$$anon$19@5d9a1340
promise: scala.concurrent.Future[List[Int]] = scala.concurrent.impl.Promise$DefaultPromise@65c78844
res0: List[Int] = List(3, 6, 9, 9)

scala>

Если вы заинтересованы в чем-то менее замкнутом вокруг изменяемого состояния (уродливом), вы можете изучить реализацию этого поведения в итерации, где вы можете удерживать промежуточное состояние.

person Asa    schedule 04.08.2015
comment
Как вы уже догадались, меня действительно интересует версия, не хранящая состояние через var. Я пытался, но похоже, что я не могу понять сигнатуру типа, связанную с Enumeratee. - person betehess; 08.08.2015
comment
Опять же, перечислители не предназначены для хранения состояния. Это просто преобразования данных от производителя (Enumerator[A]) к потребителю (Iteratee[B, V]). Таким образом, сигнатура типа Enumeratee[A, B] означает, что она отображает ввод типа A в вывод типа B. (V — это тип значения, которое в конечном итоге создает Iteratee). Если вы удалите var counter, вы можете использовать Enumeratee.map{_.length} и создать перечислитель, который предоставляет Enumerator(3, 3, 3, 0), а затем собрать его в своем Iteratee, и это будет свертываться по этому потоку для получения ваших возрастающих значений. - person Asa; 09.08.2015
comment
Вы также можете посмотреть на stackoverflow. ком/вопросы/27759548/ - person Asa; 19.08.2015