有效地遍历匹配布尔条件的数组?

时间:2015-08-26 04:22:36

标签: arrays scala

这是Scala for the Impatient提出的另一个问题

  

写一个返回a的函数lteqgt(values:Array [Int],v:Int)   三次包含小于v的值的计数,等于v,和   大于v。

我这样做的方式是

scala> def lteqgt(values: Array[Int], v: Int): (Int, Int, Int) = (values.count(_ < v), values.count(_ == v), values.count(_ > v))
lteqgt: (values: Array[Int], v: Int)(Int, Int, Int)

scala> lteqgt(Array(0,0,1,1,1,2,2), 1)
res47: (Int, Int, Int) = (2,3,2)

问题吗
我正在遍历数组3次以收集计数,有没有办法在第一时间收集值?一种惯用的方式?

3 个答案:

答案 0 :(得分:10)

这是使用foldLeft的完美案例。它将完全遍历您的集合一次,而不创建另一个集合(如groupBy所做的那样),并且与更通用的aggregate相比更简洁。

def lteqgt(values: Array[Int], v: Int): (Int, Int, Int) =
  values.foldLeft((0, 0, 0)) {
    case ((lt, eq, gt), el) =>
      if (el < v) (lt + 1, eq, gt)
      else if (el == v) (lt, eq + 1, gt)
      else (lt, eq, gt + 1)
  }

如果你想要达到最高效率,同时避免使用命令式方法,那么尾递归是可行的方法:

def lteqgt(values: Array[Int], v: Int): (Int, Int, Int) = {
  def rec(i:Int, lt:Int, eq:Int, gt:Int):(Int, Int, Int) =
    if (i == values.length) (lt, eq, gt)
    else if (values(i) < v) rec(i + 1, lt + 1, eq, gt)
    else if (values(i) == v) rec(i + 1, lt, eq + 1, gt)
    else rec(i + 1, lt, eq, gt + 1)
  rec(0, 0, 0, 0)
}

这避免了每次迭代时构造Tuple和盒装Ints。如果你感兴趣的话,整个事情会编译为java while中的 //url string NSString *ImageURL = @"yoururl"; //string to data conversion NSData *imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:ImageURL]]; //set image imageView.image = [UIImage imageWithData:imageData]; 循环。

答案 1 :(得分:2)

虽然功能性解决方案可以说更优雅,但不要忘记&#34;无聊&#34;但有效的必要解决方案。

def lteqgt(values: Array[Int], v: Int): (Int, Int, Int) = {
    var lt = 0
    var eq = 0
    var gt = 0
    values.foreach (i => {
      if      (i<v)  lt += 1
      else if (i==v) eq += 1
      else           gt += 1
    })
    (lt,eq,gt)
  }

以上可能会为每个循环生成一个函数调用,正如Aivean在下面指出的那样。为了提高效率,我们可以手动移除封盖。 (遗憾的是,编译器尚未完成此类优化)

 def lteqgt(values: Array[Int], v: Int): (Int, Int, Int) = {
   var lt = 0
   var eq = 0
   var gt = 0
   var i = 0
   while (i < values.length) {
     if      (values(i) < v ) lt += 1
     else if (values(i) == v) eq += 1
     else                     gt += 1
     i += 1
   }
   (lt,eq,gt)
  }

答案 2 :(得分:0)

与@ chi相似,但让我们超过设计规格,以便我们可以比较Ints以外的其他数据。标题是&#34; Traversing&#34;所以让我们使用Traversable,这样我们就可以分析所有其他集合。机器可能不会更快地执行此操作,但它可能是程序员时间效率。

def lteqgt[T](values:Traversable[T], v:T)(implicit cmp: Ordering[T]):(Int,Int,Int) =  {
            var l = 0;
            var e = 0; 
            var g = 0;
            for(x <- values){
                if (cmp.equiv(v,x)) e += 1
                else if (cmp.gt(x,v)) g += 1
                else l += 1
            }
        (l,e,g);
};

用法:

scala> lteqgt(List(1.0,2.0,3.0,2.5), 2.5)
res0: (Int, Int, Int) = (2,1,1)

scala> lteqgt(Array(1.0,2.0,3.0,2.5), 2.5)
res1: (Int, Int, Int) = (2,1,1)

scala> lteqgt(Vector(1.0,2.0,3.0,2.5), 2.5)
res2: (Int, Int, Int) = (2,1,1)

scala> lteqgt(Set(1.0,2.0,3.0,2.5), 2.5)
res3: (Int, Int, Int) = (2,1,1)

scala> lteqgt(1 to 100, 45)
res4: (Int, Int, Int) = (44,1,55)

scala> lteqgt(Range(0,100,3), 50)
res5: (Int, Int, Int) = (17,0,17)

scala> lteqgt(List("fee","fie","foe","fum"), "foo")
res6: (Int, Int, Int) = (3,0,1)

scala> lteqgt(None, 1)
res7: (Int, Int, Int) = (0,0,0)

scala> lteqgt(Some(2), 1)
res8: (Int, Int, Int) = (0,0,1)