我正在使用Scala编写应用程序,而我正在使用Akka流。
有一次,我需要过滤出N个元素少于N个的流,并给出N.例如,使用N=5
:
Source(List(1,2,3)).via(myFilter) // => List()
Source(List(1,2,3,4)).via(myFilter) // => List()
将成为空流,
Source(List(1,2,3,4,5)).via(myFilter) // => List(1,2,3,4,5)
Source(List(1,2,3,4,5,6)).via(myFilter) // => List(1,2,3,4,5,6)
将保持不变。
当然,我们无法知道流中的元素数量,直到它结束,等到最后再推出它可能不是最好的主意。
所以,相反,我考虑过以下算法:
但是,我不知道如何构建实现它的Flow
元素。我可以使用一些内置的Akka元素吗?
编辑:
好的,所以昨天我玩了它,我想出了类似的东西:
Flow[Int].
prefixAndTail(N).
flatMapConcat {
case (prefix, tail) if prefix.length == N =>
Source(prefix).concat(tail)
case _ =>
Source.empty[Int]
}
它会做我想要的吗?
答案 0 :(得分:1)
这可能是其中一个小状态"可以走很长的路。即使解决方案不是纯粹的功能",更新状态也将被系统的其余部分隔离并且无法访问。我认为这是scala的优点之一:当FP解决方案不明显时,你总能以孤立的方式恢复到命令式......
完成的Flow
将是多个子部分的组合。第一个Flow将您的元素分组为大小为N
的序列:
val group : Int => Flow[Int, Seq[Int], _] =
(N) => Flow[Int] grouped N
现在对于非功能性部分,只有第一个序列的大小合适才允许分组Seq
值的过滤器:
val minSizeRequirement : Int => Seq[Int] => Boolean =
(minSize) => {
var isFirst : Boolean = True
var passedMinSize : Boolean = False
(testSeq) => {
if(isFirst) {
isFirst = False
passedMinSize = testSeq.size >= minSize
passedMinSize
}
else
passedMinSize
}
}
}
val minSizeFilter : Int => Flow[Seq[Int], Seq[Int], _] =
(minSize) => Flow[Seq[Int]].filter(minSizeRequirement(minSize))
最后一步是将Seq[Int]
值转换回Int
值:
val flatten = Flow[Seq[Int]].flatMapConcat(l => Source(l))
最后,将它们组合在一起:
val combinedFlow : Int => Flow[Int, Int, _] =
(minSize) =>
group(minSize)
.via(minSizeFilter(minSize))
.via(flatten)
答案 1 :(得分:1)
也许statefulMapConcat
可以帮到你:
import akka.actor.ActorSystem
import akka.stream.scaladsl.{Sink, Source}
import akka.stream.{ActorMaterializer, Materializer}
import scala.collection.mutable.ListBuffer
import scala.concurrent.ExecutionContext
object StatefulMapConcatExample extends App {
implicit val system: ActorSystem = ActorSystem()
implicit val materializer: Materializer = ActorMaterializer()
implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
def filterLessThen(threshold: Int): (Int) => List[Int] = {
var buffering = true
val buffer: ListBuffer[Int] = ListBuffer()
(elem: Int) =>
if (buffering) {
buffer += elem
if (buffer.size < threshold) {
Nil
} else {
buffering = false
buffer.toList
}
} else {
List(elem)
}
}
//Nil
Source(List(1, 2, 3)).statefulMapConcat(() => filterLessThen(5))
.runWith(Sink.seq).map(println)
//Nil
Source(List(1, 2, 3, 4)).statefulMapConcat(() => filterLessThen(5))
.runWith(Sink.seq).map(println)
//Vector(1,2,3,4,5)
Source(List(1, 2, 3, 4, 5)).statefulMapConcat(() => filterLessThen(5))
.runWith(Sink.seq).map(println)
//Vector(1,2,3,4,5,6)
Source(List(1, 2, 3, 4, 5, 6)).statefulMapConcat(() => filterLessThen(5))
.runWith(Sink.seq).map(println)
}