如何以功能方式在嵌套集合中高效搜索

时间:2016-06-30 08:37:43

标签: scala

我希望以函数方式在Int的嵌套Vector中找到值为4的第一个元素的索引(坐标)。

val a = Vector(Vector(1,2,3), Vector(4,5), Vector(3,8,4))
a.map(_.zipWithIndex).zipWithIndex.collect{
    case (col, i) =>
        col.collectFirst {
            case (num, index) if num == 4 =>
                (i, index)
        }
}.collectFirst {
    case Some(x) ⇒ x
}

它返回:

Some((0, 1))

前4次出现的坐标。

这个解决方案非常简单,但它会降低性能,因为当我们只对第一个匹配感兴趣时,会对顶部Vector的所有元素执行嵌套col.collect

一种可能的解决方案是在模式匹配中编写一个保护。但我不知道如何在缓慢的条件下写一个后卫,并返回已经在守卫中计算过的东西。

可以做得更好吗?

2 个答案:

答案 0 :(得分:1)

可能递归吗? 如果你坚持使用Vector s,这样的东西就可以了(对于非索引的seq,你需要一个不同的方法):

   @tailrec
   findit(
     what: Int, 
     lists: IndexedSeq[IndexedSeq[Int]], 
     i: Int = 0, 
     j: Int = 0
   ): Option[(Int, Int)] = 
     if(i >= lists.length) None
     else if(j >= lists(i).length) findit(what, lists, i+1, 0)
     else if(lists(i)(j) == what) Some((i,j)) 
     else findit(what, lists, i, j+1)

答案 1 :(得分:0)

在不更改算法的情况下,您可以轻松使用Scala流,以便在找到匹配后立即退出。与序列相反,流被懒惰地评估。

只需进行类似于此的更改

a.map(_.zipWithIndex.toStream).zipWithIndex.toStream.collect{ ...

在算法更改方面,如果您可以以某种方式对数据进行排序(甚至在开始搜索之前),那么您可以使用Binary search而不是查看每个元素。

import scala.collection.Searching._

val dummy = 123

implicit val anOrdering = new Ordering[(Int, Int, Int)]{
  override def compare(x: (Int, Int, Int), y: (Int, Int, Int)): Int = Integer.compare(x._1, y._1)
}

val seqOfIntsWithPosition = a.zipWithIndex.flatMap(vectorWithIndex => vectorWithIndex._1.zipWithIndex.map(intWithIndex => (intWithIndex._1, vectorWithIndex._2, intWithIndex._2)))
val sorted: IndexedSeq[(Int, Int, Int)] = seqOfIntsWithPosition.sortBy(_._1)
val element = sorted.search((4, dummy, dummy))

这段代码不是很漂亮或可读,我很快就想展示一下它是如何完成的。