flatMap 到底做了什么?
What does flatMap do exactly?
对于学校的编码作业,我必须用 flatmap 做一些事情,但我完全不知道它的作用,我已经在线阅读了几页并阅读了我的教科书,但我仍然没有真正理解什么确实如此。我知道 map 的作用,但出于某种原因,我很难理解 flatmap。谁能帮忙?谢谢。
只是为了添加更多信息 - 当我查看在线示例时,我确实看到平面图 returns 与地图有何不同。但是当它被调用时 flatmap 实际上在做什么?平面图实际上是如何工作的? returns 结果之前它在做什么?
打个比方。
假设您有一个大袋子,里面装满了成箱鸡蛋的购物券。如果你有一个 "use the voucher to buy a carton of eggs" 的函数并且你调用了 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 扁平化嵌套值,例如如果 m
是 List
那么 join
的类型是
def joinList[A](l: List[List[A]]): List[A]
如果你有一个 monad m
并且你 map
在它之上,如果 b
是相同的 monadic 类型会怎样?例如:
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]]
,而输入是 List[Int]
。对于一系列操作来说,希望每个步骤 return 相同的输入类型是很常见的。由于 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
和 join
。
在响应式编程中,经常会遇到需要用flatMap转换Future[Future[List]][=24=的情况] 到 Future[List]。例如,您有两个函数:get Users from database 和 process retrieved Users; return 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]]
对于学校的编码作业,我必须用 flatmap 做一些事情,但我完全不知道它的作用,我已经在线阅读了几页并阅读了我的教科书,但我仍然没有真正理解什么确实如此。我知道 map 的作用,但出于某种原因,我很难理解 flatmap。谁能帮忙?谢谢。
只是为了添加更多信息 - 当我查看在线示例时,我确实看到平面图 returns 与地图有何不同。但是当它被调用时 flatmap 实际上在做什么?平面图实际上是如何工作的? returns 结果之前它在做什么?
打个比方。
假设您有一个大袋子,里面装满了成箱鸡蛋的购物券。如果你有一个 "use the voucher to buy a carton of eggs" 的函数并且你调用了 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 扁平化嵌套值,例如如果 m
是 List
那么 join
的类型是
def joinList[A](l: List[List[A]]): List[A]
如果你有一个 monad m
并且你 map
在它之上,如果 b
是相同的 monadic 类型会怎样?例如:
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]]
,而输入是 List[Int]
。对于一系列操作来说,希望每个步骤 return 相同的输入类型是很常见的。由于 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
和 join
。
在响应式编程中,经常会遇到需要用flatMap转换Future[Future[List]][=24=的情况] 到 Future[List]。例如,您有两个函数:get Users from database 和 process retrieved Users; return 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]]