而不是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)
答案 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 b
和b -> m c
等函数组合成a -> m c
(就像这里的函数一样,从String
到{{1} }})。顺便说一句,它们被称为Kleisli functions。在haskell中,只需使用Set[String]
就可以完成这些功能。在scala中,你必须更加冗长(顺便说一句,我已经改变了一些例子:我无法使用>=>
,所以我使用了Set
):
List
答案 2 :(得分:2)
您为过滤器描述的方法基本上会跳过中间集合的创建。
至少flatMap
内部集合会在函数内部创建,所以我无法想象在没有更改函数的情况下跳过该创建的任何方法。
你可以尝试使用一个视图,虽然我不确定这是否对flatMap有用。
或者你可以构建一个multiFlatMap
,它直接从函数结果中构建最终集合,而不会填充从新集合中的函数返回的中间集合。
不知道这是否可行。我至少看到一些严重的类型挑战,因为你需要传递一个函数的Seq,其中每个函数返回一个A的集合,其中A是下一个函数的输入类型。至少在任意类型和任意数量的函数的一般情况下,这听起来有些挑战。