给定一个元素列表,我需要映射(获取响应)/过滤器(检查响应是否有效)/获取(仅获取第一个有效的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版本,LazyList
https://www.scala-lang.org/blog/2018/06/13/scala-213-collections.html#lazylist-is-preferred-over-stream)
如果你查看List
的map
或filter
的定义,每个函数都必须迭代整个列表。特别是对于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)
如果要延迟map
和filter
的效果,则需要不同的数据类型。在这种情况下,您可以使用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)