Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala:有什么理由更喜欢'filter+;在“收集”上方映射?_Scala - Fatal编程技术网

Scala:有什么理由更喜欢'filter+;在“收集”上方映射?

Scala:有什么理由更喜欢'filter+;在“收集”上方映射?,scala,Scala,是否有任何理由更喜欢过滤器+映射: list.filter (i => aCondition(i)).map(i => fun(i)) 超过收集?: list.collect(case i if aCondition(i) => fun(i)) 在我看来,带有collect(单一外观)的那款看起来更快更干净。所以我总是选择收集 我想这是基于观点的,但给出了以下定义: scala> val l = List(1,2,3,4) l: List[Int] = List(1,

是否有任何理由更喜欢
过滤器+映射

list.filter (i => aCondition(i)).map(i => fun(i))
超过
收集
?:

list.collect(case i if aCondition(i) => fun(i))

在我看来,带有
collect
(单一外观)的那款看起来更快更干净。所以我总是选择收集

我想这是基于观点的,但给出了以下定义:

scala> val l = List(1,2,3,4)
l: List[Int] = List(1, 2, 3, 4)

scala> def f(x: Int) = 2*x
f: (x: Int)Int

scala> def p(x: Int) = x%2 == 0
p: (x: Int)Boolean
以下哪一项更适合阅读:

l.filter(p).map(f)

(注意,我必须修正上面的语法,因为您需要括号和
case
来添加
if
条件)

我个人觉得
过滤器
+
地图
更易于阅读和理解。这完全是您使用的确切语法的问题,但是给定
p
f
,您不必在使用
filter
map
时编写匿名函数,而在使用collect时确实需要它们

您还可以使用
flatMap

l.flatMap(i => if(p(i)) Some(f(i)) else None)
这可能是三种解决方案中最有效的,但我发现它不如
map
filter


总的来说,很难说哪一个更快,因为这取决于
scalac
和JVM最终执行了哪些优化。所有3个都应该非常接近,并且绝对不是决定使用哪一个的因素。

Scala的大多数集合都热切地应用操作,并且(除非您使用的是为您提供此功能的宏库)不会融合操作。因此
filter
后跟
map
通常会创建两个集合(即使您使用
Iterator
或类似的方法,中间形式也会临时创建,尽管一次只创建一个元素),而
collect
不会

另一方面,
collect
使用分部函数实现联合测试,分部函数在测试集合中是否有内容时比谓词(
a=>Boolean
)慢

此外,在某些情况下,读取一个文件比读取另一个文件更为清晰,并且您不关心性能或内存使用差异的2倍左右。在这种情况下,请使用更清楚的选项。通常,如果您已经有了名为的函数,那么阅读起来会更清晰

xs.filter(p).map(f)
xs.collect{ case x if p(x) => f(x) }
但是如果您是内联提供闭包,
collect
通常看起来更干净

xs.filter(x < foo(x, x)).map(x => bar(x, x))
xs.collect{ case x if foo(x, x) => bar(x, x) }
您想在过滤第一个条目的基础上挑选第二个条目(因此过滤和映射操作都非常简单),然后我们得到下表

注意:可以将惰性视图放入集合中并在那里收集操作。您并不总是可以恢复原始类型,但您可以始终使用
来获得正确的集合类型。因此
xs.view.filter(p).map(f).toVector将不会因为视图而创建中间层。这一点在下文中也得到了验证。也有人建议人们可以
xs.flatMap(x=>if(p(x))一些(f(x))其他没有)
,这是有效的事实并非如此。下面也对其进行了测试。可以通过显式创建一个生成器来避免分部函数:
val vb=Vector.newBuilder[String];foreach(x=>if(p(x))vb+=f(x));vb.result
,其结果也列在下面

在下表中,测试了三个条件:不过滤、过滤一半、过滤所有。时间已标准化为filter/map(100%=与filter/map相同的时间,越低越好)。误差范围约为+-3%

不同过滤器/映射选项的性能

====================== Vector ========================
filter/map   collect  view filt/map  flatMap   builder
   100%        44%          64%        440%      30%    filter out none
   100%        60%          76%        605%      42%    filter out half
   100%       112%         103%       1300%      74%    filter out all
因此,
filter/map
collect
通常非常接近(当你保留了很多时,
collect
获胜),
flatMap
在所有情况下都要慢得多,创建一个构建器总是赢的。(特别是对于
Vector
,这是正确的。其他集合可能有一些不同的特征,但大多数集合的趋势将是相似的,因为操作的差异是相似的。)此测试中的视图往往是成功的,但它们并不总是无缝工作(除了空箱子之外,它们实际上并不比
collect
好)


所以,底线是:当速度无关紧要时,如果它有助于清晰,则首选
filter
,然后选择
map
,或者在过滤几乎所有内容但仍希望保持功能的情况下,首选它以提高速度(因此不想使用构建器);或者使用
collect

如果
filter/map
看起来更干净,则需要展平filter的结果

def getList(x: Int) = {
  List.range(x, 0, -1)
}

val xs = List(1,2,3,4)

//Using filter and flatMap
xs.filter(_ % 2 == 0).flatMap(getList)

//Using collect and flatten
xs.collect{ case x if x % 2 == 0 => getList(x)}.flatten

我不确定性能会有多大的不同。它们都遍历完整序列一次,并且只对满足条件的项应用谓词函数,因此它们都在做相同的基本工作量。这实际上取决于编译器有多聪明,序列有多大,条件和函数有多昂贵虽然我认为第二个可能会少分配一个序列,因为它不必为
过滤器
调用的返回而烦恼。@IanMcLaird我认为这是主要的争论。第二个是否真的少分配了一个序列(从而更有效)?原因与喜欢
map+flattle
而不是
flatMap
是一样的。因此,为什么这个参数不正确:“收集”就像一个单循环,“过滤+映射”就像两个结果循环。因此(从效率上看)人们应该更喜欢“收集”。我遗漏了什么吗?(幸好你提到了第三个备选方案。)与Rex“Mr Collections”Kerr相比是不公平的,但是没有人会期望flatMap更高效。我个人的不满是使用ell作为变量名称,因为它看起来像一个。现在,这是基于意见的!所谓“filter none”是指“filter out none”,而不是“filter Catch/retains none”。第一个代码samp
====================== Vector ========================
filter/map   collect  view filt/map  flatMap   builder
   100%        44%          64%        440%      30%    filter out none
   100%        60%          76%        605%      42%    filter out half
   100%       112%         103%       1300%      74%    filter out all
def getList(x: Int) = {
  List.range(x, 0, -1)
}

val xs = List(1,2,3,4)

//Using filter and flatMap
xs.filter(_ % 2 == 0).flatMap(getList)

//Using collect and flatten
xs.collect{ case x if x % 2 == 0 => getList(x)}.flatten