3

对于学校的编码任务,我必须使用 flatmap 做一些事情,但我完全不知道它的作用,我已经在网上阅读了几页并在我的教科书中阅读,但我仍然没有真正理解它的作用。我知道 map 的作用,但由于某种原因,我很难将我的头绕在 flatmap 上。任何人都可以帮忙吗?谢谢。

只是为了添加更多信息-当我查看在线示例时,我确实看到了 flatmap 如何返回与 map 不同的东西。但是当 flatmap 被调用时,它实际上在做什么呢?平面图实际上是如何工作的?在返回结果之前它在做什么?

4

3 回答 3

6

这是一个类比。

想象一下,你有一个大袋子,里面装满了鸡蛋盒的购物券。如果您有一个功能是“使用凭证购买一箱鸡蛋”并且您调用bigBagOfVouchers.map(buyCartonOfEggs)了 ,那么您将有一袋纸箱鸡蛋。

然而,如果你打电话给bigBagOfVouchers.flatMap(buyCartonOfEggs),你会得到一袋鸡蛋——没有任何纸箱。

flatMap将结果平展一级。可能Bag[Carton[Egg]]是现在Bag[Egg]

于 2015-11-28T12:35:54.320 回答
5

函子定义具有类型的映射

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 mis Listthenjoin有类型

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 类型构造函数MList在本例中),因为它取决于mapand join

于 2015-11-28T13:10:25.027 回答
0

在响应式编程中,您经常会遇到需要使用flatMapFuture[Future[List]]转换为Future[List]的情况。例如,您有两个功能:从数据库中获取用户并处理检索到的用户;并且都返回Future[List[User]]。如果您将map应用于getprocess,结果将是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]]
于 2015-11-29T03:59:14.553 回答