无法使用理解来映射未来的列表

我有这个问题,我必须每次都解决。 我无法使用理解来映射Future中包含的内容。

例:

import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future val f = Future( List("A", "B", "C") ) for { list <- f e <- list } yield (e -> 1) 

这给了我错误:

  error: type mismatch; found : List[(String, Int)] required: scala.concurrent.Future[?] e <- list ^ 

但是,如果我这样做,它工作正常:

 f.map( _.map( (_ -> 1) ) ) 

我应该不能通过使用理解来做到这一点,是我的另一个例子,我不平坦的原因是什么? 我正在使用Scala 2.10.0。

那么,当你有一个单一的理解多个发电机,你正在平坦的结果types。 也就是说,不是得到一个List[List[T]] ,而是得到一个List[T]

 scala> val list = List(1, 2, 3) list: List[Int] = List(1, 2, 3) scala> for (a <- list) yield for (b <- list) yield (a, b) res0: List[List[(Int, Int)]] = List(List((1,1), (1,2), (1,3)), List((2,1 ), (2,2), (2,3)), List((3,1), (3,2), (3,3))) scala> for (a <- list; b <- list) yield (a, b) res1: List[(Int, Int)] = List((1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3)) 

现在,你将如何压扁Future[List[T]] ? 它不可能是一个Future[T] ,因为你会得到多个T ,而一个Future (而不是一个List )只能存储其中的一个。 顺便提一下, Option同样的问题:

 scala> for (a <- Some(3); b <- list) yield (a, b) <console>:9: error: type mismatch; found : List[(Int, Int)] required: Option[?] for (a <- Some(3); b <- list) yield (a, b) ^ 

最简单的方法是简单地嵌套多个理解:

 scala> for { | list <- f | } yield for { | e <- list | } yield (e -> 1) res3: scala.concurrent.Future[List[(String, Int)]] = scala.concurrent.im pl.Promise$DefaultPromise@4f498585 

回想起来,这个限制应该是非常明显的。 问题是几乎所有的例子都使用集合,所有集合都只是GenTraversableOnce ,所以它们可以自由混合。 CanBuildFrom ,Scala受到很多批评的CanBuildFrom机制使得混合任意集合并获得特定types成为可能,而不是GenTraversableOnce

而且,为了使事情变得更加模糊, Option可以被转换为Iterable ,只要选项不先出现,就可以将选项与集合结合起来。

但在我看来,混淆的主要根源在于,在教授理解时没有人提到这个限制。

嗯,我想我明白了。 我需要在未来包装,因为理解增加了一个平面图。

这工作:

 for { list <- f e <- Future( list ) } yield (e -> 1) 

当我在上面添加时,我还没有看到任何答案。 然而,为了扩大这一点,可以在一个理解的范围内进行工作。 不知道是否值得将来的开销虽然(编辑:通过使用成功,应该没有开销)。

 for { list1 <- f list2 <- Future.successful( list1.map( _ -> 1) ) list3 <- Future.successful( list2.filter( _._2 == 1 ) ) } yield list3 

附录,半年后。

解决这个问题的另一种方法是简单地使用assign =而不是<-当你有另一个types比初始地图返回types。

使用赋值时,该行不会被平面映射。 您现在可以自由地执行返回不同types的显式映射(或其他转换)。

如果您有几个转换,其中一个步骤的返回types与其他步骤不具有相同的返回types,但是仍然希望使用for-comprehension语法,因为这会使代码更具可读性,所以这非常有用。

 import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future val f = Future( List("A", "B", "C") ) def longRunning( l:List[(String, Int)] ) = Future.successful( l.map(_._2) ) for { list <- f e = list.map( _ -> 1 ) s <- longRunning( e ) } yield s 

您的原始版本不会编译,因为ListFuture是不同的单子。 要明白为什么这是一个问题,请考虑它的作用:

 f.flatMap(list => list.map(e => e -> 1)) 

很明显, list.map(_ -> 1)(String, Int)对的列表,所以我们的flatMap的参数是一个函数,它将string列表映射到这些对的列表。 但是我们需要一些将string列表映射到某种Future的东西。 所以这不会编译。

答案中的版本确实可以编译,但是它不会做你想要的。 它解除了这一点:

 f.flatMap(list => Future(list).map(e => e -> 1)) 

这种types这次排队,但我们没有做任何有趣的事情 – 我们只是将价值从Future拉回来,放回到Future ,并映射到结果。 所以当我们想要Future[List[(String, Int)]]Future[(List[String], Int)]我们最终得到了Future[(List[String], Int)]types的东西。

你正在做的是一种双映射操作,有两个(不同的)嵌套monad,这不是一个for comprehension将帮助你。 幸运的是, f.map(_.map(_ -> 1))完全符合您的要求,且清晰简洁。

我发现这个表格比串行映射或串行良率更可读:

 for (vs <- future(data); xs = for (x <- vs) yield g(x) ) yield xs 

以tupling地图为代价:

 f.map((_, xs)).map(_._2) 

或者更确切地说:

 f.map((vs: List[Int]) => (vs, for (x <- vs) yield g(x))).map(_._2)