【问题标题】:Scala, Collection.searching with user-defined implicit Ordering[]Scala,Collection.searching 与用户定义的隐式排序 []
【发布时间】:2021-05-17 05:58:28
【问题描述】:

我需要对一组自定义案例类执行二进制搜索。这应该像调用scala.collection.Searching中定义的search函数一样简单:

如你所见,如果我调用搜索方法的集合是一个索引序列,则执行二分查找。

现在,我需要创建我的自定义 Ordering[B] 参数,并且我想将它显式传递给 search 函数(我不希望它采用任何从上下文推断的隐式参数)。

我有以下代码:

// File 1
case class Person(name: String, id: Int)
object Person{ 
  val orderingById: Ordering[Person] = Ordering.by(e => e.id)
}

// File 2 (same package)
for(i <- orderedId.indices) {
  // orderedId is an array of Int
  // listings is an array of Person
  val listingIndex = listings.search(orderedId(i))(Person.orderingById)
  ...
}

我收到以下错误:

类型不匹配。必需:Ordering[Any],找到:Ordering[Nothing]

所以,我尝试以这种方式更改实现:

// file 1
object Person{
  implicit def orderingById[A <: Person] : Ordering[A] = {
      Ordering.by(e => e.id)
  }
}
//file 2 as before

这次出现如下错误:

类型不匹配。必填:Ordering[Any],找到:Ordering[Person]

为什么会这样?至少在第二种情况下,它应该从Any转换为Person吗?

【问题讨论】:

  • listing的类型是什么orderedId的类型是什么?
  • Listing -> PersonorderedId 的数组 -> Int 的数组。它被写成代码中的注释,但可能不够明显/可见。
  • 您正在一组人员中搜索一个 int,这将永远无法工作。你想要这个:listings.find(p =&gt; p.id == orderedId(i)) 并删除自定义排序,因为它不需要 - 另外,请不要使用 Arrays,也请不要使用命令式循环,还请查看Scaladoc。此外,如果您要不断地通过键搜索事物,最好将其转换为 Map-

标签: arrays scala sorting search implicit


【解决方案1】:

遵循类型规范。

如果您想在Person 元素的集合上使用.search(),那么第一个search 参数应该是Person(或其超类)。

val listingIndex =
  listings.search(Person("",orderedId(i)))(Person.orderingById)

或者,把它放在更完整和简洁的上下文中:

import scala.collection.Searching.SearchResult

case class Person(name: String, id: Int)

val listings: Array[Person] = ...
val orderedId: Array[Int]   = ...

for(id <- orderedId) {
  val listingIndex: SearchResult =
    listings.search(Person("",id))(Ordering.by(_.id))
}

【讨论】:

    【解决方案2】:

    我将添加一些内容来详细说明您的错误。首先,请注意Searching.search 已被弃用,并带有弃用消息:

    搜索方法直接在SeqOps 上定义,不再需要scala.collection.Searching

    search 现在在IndexedSeqOps 上定义。我们来看看签名:

    final def search[B >: A](elem: B)(implicit ord: Ordering[B])
    

    当你打电话时:

    listings.search(orderedId(i))(Person.orderingById)
    

    orderedId(i) 的结果是Int。所以上面签名中的B就是IntInt的定义是:

    final abstract class Int private extends AnyVal
    

    APerson,因为 listingArray[Person] 类型。因此,search 正在为IntPerson 寻找一个共同的根。这个公共根是Any,因此您会收到此错误。克服它的一种方法是定义从 IntPerson 的隐式转换:

    object Person{
      val orderingById: Ordering[Person] = Ordering.by(e => e.id)
    
      implicit def apply(id: Int): Person = {
        Person("not defined", id)
      }
    }
    

    然后是:

    val listings = Array(Person("aa", 1), Person("bb", 2), Person("dd", 4))
    val orderedId = 1.to(6).toArray
    
    for(i <- orderedId.indices) {
      // orderedId is an array of Int
      // listings is an array of Person
      listings.search[Person](orderedId(i))(Person.orderingById) match {
        case Found(foundIndex) =>
          println("foundIndex: " + foundIndex)
        case InsertionPoint(insertionPoint) =>
          println("insertionPoint: " + insertionPoint)
      }
    }
    

    将产生:

    foundIndex: 0
    foundIndex: 1
    insertionPoint: 2
    foundIndex: 2
    insertionPoint: 3
    insertionPoint: 3
    

    代码在Scastie 中运行。

    【讨论】:

      猜你喜欢
      • 2014-11-02
      • 2012-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-04
      • 2016-01-23
      • 2020-07-24
      相关资源
      最近更新 更多