对于学校的编码任务,我必须使用 flatmap 做一些事情,但我完全不知道它的作用,我已经在网上阅读了几页并在我的教科书中阅读,但我仍然没有真正理解它的作用。我知道 map 的作用,但由于某种原因,我很难将我的头绕在 flatmap 上。任何人都可以帮忙吗?谢谢。
只是为了添加更多信息-当我查看在线示例时,我确实看到了 flatmap 如何返回与 map 不同的东西。但是当 flatmap 被调用时,它实际上在做什么呢?平面图实际上是如何工作的?在返回结果之前它在做什么?
这是一个类比。
想象一下,你有一个大袋子,里面装满了鸡蛋盒的购物券。如果您有一个功能是“使用凭证购买一箱鸡蛋”并且您调用bigBagOfVouchers.map(buyCartonOfEggs)
了 ,那么您将有一袋纸箱鸡蛋。
然而,如果你打电话给bigBagOfVouchers.flatMap(buyCartonOfEggs)
,你会得到一袋鸡蛋——没有任何纸箱。
flatMap
将结果平展一级。可能Bag[Carton[Egg]]
是现在Bag[Egg]
。
函子定义具有类型的映射
trait Functor[F[_]] {
def map[A, B](f: A => B)(v: F[A]): F[B]
}
Monad 是支持两个额外操作的函子:
trait Monad[M[_]] extends Functor[M] {
def pure[A](v: A): M[A]
def join[A](m: M[M[A]]): M[A]
}
Join 扁平化嵌套值,例如 if m
is List
thenjoin
有类型
def joinList[A](l: List[List[A]]): List[A]
如果你有一个单子m
并且你map
超过了它,如果b
是相同的单子类型会发生什么?例如:
def replicate[A](i: Int, value: A): List[A] = ???
val f = new Functor[List] {
def map[A, B](f: A => B)(v: List[A]) = v.map(f)
}
然后
f.map(x => replicate(x, x))(List(1,2,3)) == List(List(1), List(2,2), List(3,3,3))
这有类型List[List[Int]]
,而输入是 a List[Int]
。希望每个步骤返回相同的输入类型的操作链是相当常见的。由于List
也可以制成 monad,因此您可以使用以下方法轻松创建这样的列表join
:
listMonad.join(List(List(1), List(2,2), List(3,3,3))) == List(1,2,2,3,3,3)
现在您可能想要编写一个函数来将这两个操作合二为一:
trait Monad[M] {
def flatMap[A, B](f: A => M[B])(m: M[A]): M[B] = join(map(f)(m))
}
那么你可以简单地做:
listMonad.flatMap(List(1,2,3), x => replicate(x, x)) == List(1,2,2,3,3,3)
究竟做什么flatMap
取决于 monad 类型构造函数M
(List
在本例中),因为它取决于map
and join
。
在响应式编程中,您经常会遇到需要使用flatMap将Future[Future[List]]转换为Future[List]的情况。例如,您有两个功能:从数据库中获取用户并处理检索到的用户;并且都返回Future[List[User]]。如果您将map应用于get和process,结果将是Future[Future[List[User]]],这没有任何意义。相反,您应该使用 flatMap:
def main(): Future[List[User]] = getUsers flatMap processUsers
def getUsers: Future[List[User]]
def processUsers(users: List[User]): Future[List[User]]