Scala 将列表[A]转换为列表[B]并进行展平的流,给定另一个将A转换为列表[B]的流

Scala 将列表[A]转换为列表[B]并进行展平的流,给定另一个将A转换为列表[B]的流,scala,akka,akka-stream,Scala,Akka,Akka Stream,我有一个“内部”流程,它将A转换为List[B]。我想创建一个流,通过调用每个List[a]的每个元素上的“内部”流,将List[a]转换为List[B],然后将结果展平 有关说明,请参见下面的测试用例(依赖项是最新的scalatest和akka流,A这里是String,B这里是Char,“内部”流是stringToCharacters) 测试通过,但实现不是惯用的Akka流代码,因为它具体化/运行子流 import akka.NotUsed import akka.actor.ActorSys

我有一个“内部”流程,它将
A
转换为
List[B]
。我想创建一个流,通过调用每个
List[a]
的每个元素上的“内部”流,将
List[a]
转换为
List[B]
,然后将结果展平

有关说明,请参见下面的测试用例(依赖项是最新的
scalatest
akka流
A
这里是
String
B
这里是
Char
,“内部”流是
stringToCharacters

测试通过,但实现不是惯用的Akka流代码,因为它具体化/运行子流

import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Flow, Sink, Source}
import org.scalatest.FlatSpec
import org.scalatest.Matchers._
import org.scalatest.concurrent.ScalaFutures.whenReady
import scala.concurrent.ExecutionContext.Implicits.global

class TestSpec extends FlatSpec {
  implicit val system = ActorSystem()
  implicit val materializer = ActorMaterializer()

  /* This is just for illustration and testing purposes, `flattenFlows` should support various inner flows */
  val stringToCharacters: Flow[String, List[Char], NotUsed] =
    Flow[String].map(x => x.toList)

  /* FIXME: I'm looking for a better implementation of the following function, please don't change the signature */
  def flattenFlows[A, B](innerFlow: Flow[A, List[B], NotUsed]): Flow[List[A], List[B], NotUsed] = {
    Flow[List[A]]
      .mapAsync(Int.MaxValue) { x =>
        Source(x).via(innerFlow).runWith(Sink.seq).map(_.flatten.toList)
      }
  }

  it should "flatten flows" in {
    val input = List(
      List("ab", "cd"),
      List("ef", "gh")
    )

    val result = Source(input).via(flattenFlows(stringToCharacters)).runWith(Sink.seq)

    val expected = List(
      List('a', 'b', 'c', 'd'),
      List('e', 'f', 'g', 'h')
    )

    whenReady(result) { x =>
      x.toList shouldEqual expected
    }
  }
}
请建议一个更好的、惯用的实现,它不涉及运行子流

import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Flow, Sink, Source}
import org.scalatest.FlatSpec
import org.scalatest.Matchers._
import org.scalatest.concurrent.ScalaFutures.whenReady
import scala.concurrent.ExecutionContext.Implicits.global

class TestSpec extends FlatSpec {
  implicit val system = ActorSystem()
  implicit val materializer = ActorMaterializer()

  /* This is just for illustration and testing purposes, `flattenFlows` should support various inner flows */
  val stringToCharacters: Flow[String, List[Char], NotUsed] =
    Flow[String].map(x => x.toList)

  /* FIXME: I'm looking for a better implementation of the following function, please don't change the signature */
  def flattenFlows[A, B](innerFlow: Flow[A, List[B], NotUsed]): Flow[List[A], List[B], NotUsed] = {
    Flow[List[A]]
      .mapAsync(Int.MaxValue) { x =>
        Source(x).via(innerFlow).runWith(Sink.seq).map(_.flatten.toList)
      }
  }

  it should "flatten flows" in {
    val input = List(
      List("ab", "cd"),
      List("ef", "gh")
    )

    val result = Source(input).via(flattenFlows(stringToCharacters)).runWith(Sink.seq)

    val expected = List(
      List('a', 'b', 'c', 'd'),
      List('e', 'f', 'g', 'h')
    )

    whenReady(result) { x =>
      x.toList shouldEqual expected
    }
  }
}

您可以结合使用
flatMapConcat
fold

  def flattenFlows[A, B](innerFlow: Flow[A, List[B], NotUsed]): Flow[List[A], List[B], NotUsed] =
    Flow[List[A]].flatMapConcat{ x => Source(x).via(innerFlow).fold(List.empty[B])(_ ::: _)}

您是否正在寻找
mapcont
mapcont
如果我有一个函数可以将
a
转换为
List[B]
,但我没有函数,只有一个流。“.mapsync(Int.MaxValue){x=>”绝对不是。这个函数是我希望以更好的方式重写的函数。如果你告诉我这只是意味着从
Int.MaxValue
到更小的值,我相信你的话,然后就这么做。但是我认为可以有完全不同的方法。测试只是说明问题。你的建议是这样的es不会调用
stringToCharacters
“内部”流。如果我的目标只是从
List[String]
List[Char]
,我当然会按照你的建议去做,但我的实际问题更复杂。通过建议
flatMapConcat
改变了我的答案,这仍然解决了一个与问题不同的问题(因此您更改了
List
)的签名。这假设我有一个可用的函数
List[a]=>a
,可能不是这样(实际上在我的实际用例中不可用)。再次编辑了我的答案,第三次迭代:)好的,这个函数有效,看起来比我的更好。谢谢!