Scala 阿克卡溪流&x2014;按流中的元素数进行过滤

Scala 阿克卡溪流&x2014;按流中的元素数进行过滤,scala,stream,akka,akka-stream,Scala,Stream,Akka,Akka Stream,我正在用Scala编写一个应用程序,我正在使用Akka streams 在某一点上,我需要过滤出元素少于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) // => Li

我正在用Scala编写一个应用程序,我正在使用Akka streams

在某一点上,我需要过滤出元素少于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)
将保持不变

当然,在结束之前,我们无法知道流中元素的数量,而等到流结束后再推动流通过可能不是最好的主意

因此,我考虑了以下算法:

  • 对于前N-1个元素,只需缓冲它们,不需要进一步传递
  • 如果输入流在到达第n个元素之前完成,则输出空流
  • 如果输入流到达第N个元素,则输出缓冲的N-1个元素,然后输出第N个元素,然后传递以下所有元素
  • 但是,我不知道如何构建实现它的
    元素。是否有一些内置的Akka元素我可以使用

    编辑:

    好吧,我昨天玩过它,我想出了这样的主意:

    Flow[Int].
      prefixAndTail(N).
      flatMapConcat {
        case (prefix, tail) if prefix.length == N =>
          Source(prefix).concat(tail)
        case _ =>
          Source.empty[Int]
      }
    

    它会做我想要的吗?

    这可能是一个小“状态”可以走很长一段路的例子。即使解决方案不是“纯功能性的”,更新状态也会被隔离,系统的其余部分无法访问。我认为这是scala的优点之一:当FP解决方案不明显时,您总是可以以一种孤立的方式恢复到命令式

    完成的
    流程
    将是多个子部分的组合。第一个流只是将元素分组为大小为
    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)
    

    也许
    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)
    }
    
    导入akka.actor.ActorSystem
    导入akka.stream.scaladsl.{Sink,Source}
    导入akka.stream.{Actormatarializer,Materializer}
    导入scala.collection.mutable.ListBuffer
    导入scala.concurrent.ExecutionContext
    对象状态FulMapConcateXample扩展应用程序{
    隐式val系统:ActorSystem=ActorSystem()
    隐式val-materializer:materializer=ActorMaterializer()
    隐式val ec:ExecutionContext=scala.concurrent.ExecutionContext.Implicits.global
    def filterLessThen(阈值:Int):(Int)=>列表[Int]={
    var缓冲=真
    val buffer:ListBuffer[Int]=ListBuffer()
    (元素:Int)=>
    if(缓冲){
    缓冲区+=元素
    if(buffer.size<阈值){
    无
    }否则{
    缓冲=假
    缓冲区列表
    }
    }否则{
    列表(elem)
    }
    }
    //零
    源(列表(1、2、3)).statefulMapConcat(()=>filterLessThen(5))
    .runWith(Sink.seq).map(println)
    //零
    源(列表(1,2,3,4)).statefulMapConcat(()=>filterlessen(5))
    .runWith(Sink.seq).map(println)
    //向量(1,2,3,4,5)
    源(列表(1,2,3,4,5)).statefulMapConcat(()=>filterlessen(5))
    .runWith(Sink.seq).map(println)
    //向量(1,2,3,4,5,6)
    源(列表(1,2,3,4,5,6)).statefulMapConcat(()=>filterlessen(5))
    .runWith(Sink.seq).map(println)
    }