Scala 给定列表映射/过滤器/获取,而无需迭代整个列表



给定一个元素列表,我需要映射(获取响应)/过滤器(检查响应是否有效)/获取(仅获取第一个有效的n个元素),而无需迭代整个列表。

例如,我有一个包含 10 个元素的列表。
第 1 个有效,第 2 个无效
,第 3 个有效

第 4 个及后续有效

示例代码

List(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
.map{x => println("Some req/res"); x}
.filter{_ % 2 == 0} //filter for valid responses
.take(3) //take first 3 valid ones

我希望println("一些要求/res")只打印4次,但它打印了所有20次。
如何在不遍历整个列表的情况下获取前 n 个有效元素?

现在我正在使用带有累加器列表的 for 循环,并检查其大小,直到它充满有效的响应。 我正在寻找更实用的方法。

谢谢。

您希望使结果成为延迟计算的集合。一种简单的方法是添加.view

val res = List(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
.view
.map{x => println("Some req/res"); x}
.filter{_ % 2 == 0} //filter for valid responses
.take(3) //take first 3 valid ones
//res: scala.collection.View[Int] = View(<not computed>)

您会注意到,现在您没有println()输出。那是因为结果还没有被"强迫"。但这仍然是一个很好的结果,你可以进一步处理。

如果只是想看到结果,有很多方法可以强制评估。这是一个简单的问题。

res.toList
//Some req/res
//Some req/res
//Some req/res
//Some req/res
//Some req/res
//Some req/res
//res0: List[Int] = List(2, 4, 6)

正如其他人所说,.map.filter都需要遍历所有列表。

仅打印 3 次的简单方法是更改操作顺序:

List(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
.filter{_ % 2 == 0} //filter for valid responses
.take(3) //take first 3 valid ones
.map{x => println("Some req/res"); x}

这样,它只会打印 3 次(但过滤器仍将遍历所有集合

如果你真的想避免遍历整个列表,请尝试使用惰性集合,如Stream(如果你使用的是较新的scala版本,LazyListhttps://www.scala-lang.org/blog/2018/06/13/scala-213-collections.html#lazylist-is-preferred-over-stream)

如果你查看Listmapfilter的定义,每个函数都必须迭代整个列表。特别是对于map它将返回一个新List,该 由于将给定的函数 f 应用于此列表的每个元素并收集结果。

然后,map将返回一个新列表,其值与初始列表相同,其副作用是为每个元素打印一次"一些 req/res"。

res0: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
scala>.map{x => println("Some req/res"); x}
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
Some req/res
res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)

然后,filter将生成一个新列表,其中仅返回与谓词匹配的元素。

scala> .filter{_ % 2 == 0}
res2: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

最后,take(3)将创建一个最多包含 3 个元素的新列表。

scala> .take (3)
res3: List[Int] = List(2, 4, 6)

如果要延迟mapfilter的效果,则需要不同的数据类型。在这种情况下,您可以使用Stream,但使用最终toList将结果收集到列表中。

scala> List(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20).toStream
res8: scala.collection.immutable.Stream[Int] = Stream(1, ?)
scala> .map{x => println("Some req/res"); x}
Some req/res
res9: scala.collection.immutable.Stream[Int] = Stream(1, ?)
scala> .filter{_ % 2 == 0}
Some req/res
res10: scala.collection.immutable.Stream[Int] = Stream(2, ?)
scala> .take(3)
res11: scala.collection.immutable.Stream[Int] = Stream(2, ?)
scala> .toList
Some req/res
Some req/res
Some req/res
Some req/res
res13: List[Int] = List(2, 4, 6)

最新更新