我是Scala的新手,并试图找出过滤器的最佳方法。映射集合。这是解释我问题的玩具示例。
方法1:这非常糟糕,因为我在列表中迭代两次并在每次迭代中计算相同的值。
val N = 5
val nums = 0 until 10
val sqNumsLargerThanN = nums filter { x: Int => (x * x) > N } map { x: Int => (x * x).toString }
方法2:这稍微好些但我仍然需要计算(x * x)
两次。
val N = 5
val nums = 0 until 10
val sqNumsLargerThanN = nums collect { case x: Int if (x * x) > N => (x * x).toString }
那么,是否可以计算这个而不重复两次收集并避免重复相同的计算?
答案 0 :(得分:7)
可以使用foldRight
nums.foldRight(List.empty[Int]) {
case (i, is) =>
val s = i * i
if (s > N) s :: is else is
}
foldLeft
也会实现类似的目标,但结果列表的顺序会相反(由于foldLeft
的相关性。
或者,如果您想使用Scalaz
import scalaz.std.list._
import scalaz.syntax.foldable._
nums.foldMap { i =>
val s = i * i
if (s > N) List(s) else List()
}
答案 1 :(得分:4)
典型的方法是使用iterator
(如果可能)或view
(如果iterator
无法工作)。这并不完全完全避免两次遍历,但它确实避免了创建一个完整大小的中间集合。然后,您先map
然后filter
,然后再根据需要再map
:
xs.iterator.map(x => x*x).filter(_ > N).map(_.toString)
这种方法的优点在于它非常易于阅读,并且由于没有中间集合,因此效率相当高。
如果您因为这是性能瓶颈而要求,那么答案通常是编写尾递归函数或使用旧式while循环方法。例如,在你的情况下
def sumSqBigN(xs: Array[Int], N: Int): Array[String] = {
val ysb = Array.newBuilder[String]
def inner(start: Int): Array[String] = {
if (start >= xs.length) ysb.result
else {
val sq = xs(start) * xs(start)
if (sq > N) ysb += sq.toString
inner(start + 1)
}
}
inner(0)
}
您还可以在inner
中向前传递参数,而不是使用外部构建器(对于总和尤其有用)。
答案 2 :(得分:3)
一种非常简单的方法,只进行一次乘法运算。它也很懒,所以它只在需要时执行代码。
var m=[{"quiz_id":"3","_id":"1","option_in_json":"[{\"option\":\"1\",\"is_answer\":false},{\"option\":\"2\",\"is_answer\":true}]","question":"1+1"}];
alert(m[0].option_in_json);
查看here,了解nums.view.map(x=>x*x).withFilter(x => x> N).map(_.toString)
和filter
之间的差异。
答案 3 :(得分:2)
您可以使用collect将部分函数应用于其定义的集合的每个值。您的示例可以重写如下:
val sqNumsLargerThanN = nums collect {
case (x: Int) if (x * x) > N => (x * x).toString
}
答案 4 :(得分:2)
我还没有确认这是真正的单程,但是:
val sqNumsLargerThanN = nums flatMap { x =>
val square = x * x
if (square > N) Some(x) else None
}
答案 5 :(得分:2)
考虑这一点是为了理解,
for (x <- 0 until 10; v = x*x if v > N) yield v.toString
展开到范围内的flatMap
和(懒惰)withFilter
到一次只计算的方格,并产生一个带有过滤结果的集合。要注意一个迭代和一个平方的计算是必需的(除了创建范围)。
答案 6 :(得分:0)
您可以使用flatMap
。
val sqNumsLargerThanN = nums flatMap { x =>
val square = x * x
if (square > N) Some(square.toString) else None
}
或者使用Scalaz,
import scalaz.Scalaz._
val sqNumsLargerThanN = nums flatMap { x =>
val square = x * x
(square > N).option(square.toString)
}
通过一次迭代解决了如何执行此操作的问题。这在流数据时非常有用,例如Iterator。
但是......如果你想要绝对最快的实现,那就不是了。事实上,我怀疑你会使用一个可变的ArrayList和一个while循环。但只有在剖析后才能确定。在任何情况下,这都是另一个问题。
答案 7 :(得分:0)
使用for comprehension可以工作:
val sqNumsLargerThanN = for {x <- nums if x*x > N } yield (x*x).toString
另外,我不确定,但我认为scala编译器在地图之前对过滤器很聪明,如果可能的话只会进行1次传递。
答案 8 :(得分:-2)
我也是初学者,如下所示
for(y<-(num.map(x=>x*x)) if y>5 ) { println(y)}