Scala中的多个flatMaps

时间:2012-09-02 05:06:35

标签: scala functional-programming scala-collections

而不是xs map f map g而是更有效地编写xs map { x => g(f(x)) },并且类似于多个filter操作。

如果我连续两个或多个flatMap,有没有办法将它们合并为一个,那可能更有效率? e.g。

def f(x: String) = Set(x, x.reverse)
def g(x: String) = Set(x, x.toUpperCase)

Set("hi", "bye") flatMap f flatMap g  
  // Set(bye, eyb, IH, BYE, EYB, ih, hi, HI)

3 个答案:

答案 0 :(得分:11)

为了进行这样的转换,我们需要使用操作所具有的一些标识。例如,正如您所写,map具有身份

map f ∘ map g ≡ map (f ∘ g)

(其中代表函数组合 - 请参阅Function1。compose(...); 代表表达式的等价物)。这是因为map的类可以被视为functors,因此map的任何合理实现都必须保留此属性。

另一方面,拥有flatMap并且如何创建某种单元素实例(如创建单例Set)的类通常会形成monad。所以我们可以尝试从monad规则中推导出一些变换。但我们可以推导出flatMap重复应用的唯一身份是

(set flatMap f) flatMap g ≡ x flatMap { y => f(y) flatMap g }

这是flatMap组成的associativity relationship种类。这对优化计算没有多大帮助(实际上它可能会使情况变得更糟)。因此,结论是,flatMap没有类似的一般“优化”身份。

底线是:给Set.flatMap的每个函数为其应用的每个元素创建一个新的Set。除非我们完全忘记使用合成flatMap并以某种不同的方式解决问题,否则我们无法避免创建此类中间集。通常情况下这不值得,因为撰写flatMap(或使用for(...) yield ..)更清晰,更易读,而且速度权衡通常不是一个大问题。

答案 1 :(得分:5)

scalaz中,有一种方法可以将a -> m bb -> m c等函数组合成a -> m c(就像这里的函数一样,从String到{{1} }})。顺便说一句,它们被称为Kleisli functions。在haskell中,只需使用Set[String]就可以完成这些功能。在scala中,你必须更加冗长(顺便说一句,我已经改变了一些例子:我无法使用>=>,所以我使用了Set ):

List

答案 2 :(得分:2)

您为过滤器描述的方法基本上会跳过中间集合的创建。

至少flatMap内部集合会在函数内部创建,所以我无法想象在没有更改函数的情况下跳过该创建的任何方法。

你可以尝试使用一个视图,虽然我不确定这是否对flatMap有用。

或者你可以构建一个multiFlatMap,它直接从函数结果中构建最终集合,而不会填充从新集合中的函数返回的中间集合。

不知道这是否可行。我至少看到一些严重的类型挑战,因为你需要传递一个函数的Seq,其中每个函数返回一个A的集合,其中A是下一个函数的输入类型。至少在任意类型和任意数量的函数的一般情况下,这听起来有些挑战。