Scala Akka Streams:如何计算不同来源中的不同值

Scala Akka Streams:如何计算不同来源中的不同值,scala,akka-stream,Scala,Akka Stream,我有N(每个具体化可能不同)有限的排序数字源。我需要这些数字流的结果,以及它们出现的次数 例如: 1,3,5,7 -> | | 1,5,7 -> | ? | -> (1,2),(2,1),(3,1),(4,1),(5,3),(7,2) 2,4,5 -> | | 如何实现这一点?基本上,您可以将多个源合并为一个源,然后聚合数据。 以下是我用来组合多个源的帮助对象和方法: object ConcatSources { def apply[T](sourc

我有
N
(每个具体化可能不同)有限的排序数字源。我需要这些数字流的结果,以及它们出现的次数

例如:

1,3,5,7 -> |   |
1,5,7   -> | ? | -> (1,2),(2,1),(3,1),(4,1),(5,3),(7,2)
2,4,5   -> |   |

如何实现这一点?

基本上,您可以将多个源合并为一个源,然后聚合数据。 以下是我用来组合多个源的帮助对象和方法:

object ConcatSources {
  def apply[T](sources: Seq[Source[T, NotUsed]]): Source[T, NotUsed] = {

    sources match {
      case first :: second :: rest =>
        Source.combine(first, second, rest: _*)(Concat(_))
      case first :: _ =>
        first
      case Nil =>
        Source.empty
    }
  }
}
然后,您的任务解决方案:

  val sources: Seq[Source[Int, NotUsed]] = Seq(
    Source[Int](List(1, 3, 5, 7)),
    Source[Int](List(1, 5, 7)),
    Source[Int](List(2, 4, 5))
    )
  ConcatSources(sources).fold(Map[Int, Int]()) { (map, i) =>
    map.updated(i, map.getOrElse(i, 0) + 1)
  }.runForeach(map => println(map.toList))

另一个答案是将所有源连接成一个流

1,3,5,7,1,5,7,2,4,5
…并通过折叠累积元素及其各自计数的映射。为了计算计数,必须消耗整个流:换句话说,如果不消耗整个流,就无法知道任何整数的出现次数

另一种方法是使用将预先排序的流合并为一个单独的流,该流已排序

1,1,2,3,4,5,5,5,7,7
…然后使用生成计数:

val src1 = Source(List(1, 3, 5, 7))
val src2 = Source(List(1, 5, 7))
val src3 = Source(List(2, 4, 5))

def mergeSortedSources(sources: List[Source[Int, NotUsed]]): Source[Int, NotUsed] =
  sources.foldLeft(Source.empty[Int])(_ mergeSorted _)
         .concat(Source.single(0)) // this ending element is needed to print the last pair

mergeSortedSources(List(src1, src2, src3))
  .statefulMapConcat { () =>
    var prev: Option[Int] = None
    var count = 0

    x =>
      prev match {
        case None | Some(`x`) =>
          count = count + 1
          prev = Some(x)
          Nil
        case Some(oldElem) =>
          val oldCount = count
          count = 1
          prev = Some(x)
          (oldElem -> oldCount) :: Nil
      }
  }.runForeach(println)
运行上述代码将打印以下内容:

(1,2)
(2,1)
(3,1)
(4,1)
(5,3)
(7,2)

由于合并的流是排序的,因此在处理流时按连续顺序计算计数。也就是说,在确定
2
的出现次数之前,先确定
1
的出现次数,依此类推。

现在,我基于合并图实现了一些不同的逻辑(但这是问题的共同点)。所有这些都有效(但需要一些改进)。那么,它是否可以在没有自己的graph的情况下实现?您的意思是:没有graph DSL?是的,请看我的回答,正如我正确理解的,您只需使用
内存
折叠
流,如“源到列表,然后列表+列表,然后处理大列表”。这是一个
流处理
?我尝试为有序流实现类似
内部连接
,因此我也尝试保留顺序以便进一步处理Hi Andrey,请参阅Source.combine和Flow.fold方法的注释,我看不出它不应该是流处理的原因。你是对的,只有地图才有物化。我为我的实际任务简化了一个问题,所以你们的回答对原来的问题是正确的。