应用于List [flat [T]]时flatMap的行为

时间:2015-03-23 15:29:19

标签: scala functional-programming

我们来看看这段代码:

scala> val a = List(Some(4), None)
a: List[Option[Int]] = List(Some(4), None)
scala> a.flatMap( e=> e)
List[Int] = List(4)

为什么在flatMap上使用{ e => e }函数List[Option[T]]会返回List[T]并移除None个元素?

具体来说,它背后的概念推理是什么?它是基于函数式编程中的一些现有理论吗?这种行为在其他函数式语言中是否常见?

虽然这确实很有用,但同时确实感觉有点神奇而又随意。

编辑:

感谢您的反馈和回答。 我重写了我的问题,更加强调问题的概念性。而不是Scala特定的实现细节,我更感兴趣的是了解它背后的正式概念。

3 个答案:

答案 0 :(得分:7)

我认为您的意思是使用flatMap同时支持映射和过滤:

scala> List(1, 2).flatMap {
     |   case i if i % 2 == 0 => Some(i)
     |   case i => None
     | }
res0: List[Int] = List(2)

这是有效的,因为Option的{​​{3}}包含从Option[A]Iterable[A]的隐式转换,即GenTraversableOnce[A],这是{{1}期望作为其参数函数的返回类型。

这是一个方便的习惯用法,但它并不存在于其他函数式语言中(至少是我熟悉的),因为它依赖于Scala奇怪的子类型,隐式转换等组合。例如Haskell提供但是,通过companion object的类似功能。

答案 1 :(得分:4)

让我们首先看一下Scaladoc for Option的伴随对象。在那里我们看到了一个隐含的转换:

implicit def option2Iterable[A](xo: Option[A]): Iterable[A]

这意味着任何选项都可以隐式转换为Iterable,从而产生具有零个或一个元素的集合。如果您需要Option[A] Iterable[A],编译器将为您添加转换。

在你的例子中:

val a = List(Some(4), None)
a.flatMap(e => e)

我们正在调用List.flatMap,它接受​​一个函数A => GenTraversableOnce[B]。在这种情况下,AOption[Int]B将被推断为Int,因为通过隐式转换的魔力,e在该函数中返回时将会{是从Option[Int]转换为Iterable[Int]GenTraversableOnce的子类型。)

此时,我们基本上完成了以下工作:

List(List(1), Nil).flatMap(e => e)

或者,使我们隐式明确:

List(Option(1), None).flatMap(e => e.toList)

flatMap然后对选项进行处理,就像它对Scala中的任何线性集合一样:采用A => List[B]的函数(再次,简化)并生成List[B]的扁平集合,将嵌套集合嵌套在流程中。

答案 2 :(得分:0)

一个简单的答案是:flatMap类型的List方法被定义为与更通用的函数类型一起使用,而不仅仅是仅产生List[B]的函数。结果类型。

一般结果类型为IterableOnce[B],如faltMap方法签名:final def flatMap[B](f: (A) => IterableOnce[B]): List[B]中所示。 flatMap implementation非常简单,因为它将f函数应用于每个元素,并在嵌套的while循环中迭代结果。嵌套循环中的所有结果都将添加到List[B]类型的结果中。

因此,flatMap可以与从每个列表元素生成IterableOnce[B]结果的任何函数一起使用。 IterableOnce是一个特性,它定义了一个最小接口,该接口被所有可迭代类(包括所有集合类型(SetMap等)和Option类继承。

Option class implementationcollection.Iterator.empty返回None,为collection.Iterator.single(x)返回Some(x)。因此,flatMap方法将跳过None元素。

该问题使用identity函数。当目的是平整flatten元素时,最好使用iterable方法。

scala> val a = List(Some(4), None)
a: List[Option[Int]] = List(Some(4), None)

scala> a.flatten
res0: List[Int] = List(4)