【问题标题】:How to add 'Array[Ordered[Any]]' as a method parameter如何添加 'Array[Ordered[Any]]' 作为方法参数
【发布时间】:2013-04-18 14:53:00
【问题描述】:

下面是一个用 Scala 编写的选择排序的实现。

ss.sort(arr) 行导致此错误: 类型不匹配;找到:需要 Array[String]:Array[Ordered[Any]]

既然 Ordered 类型是由 StringOps 继承的,这种类型不应该被推断吗? 如何将字符串数组添加到 sort() 方法?

这里是完整的代码:

object SelectionSortTest {

  def main(args: Array[String]){

    val arr = Array("Hello","World")

    val ss = new SelectionSort()
    ss.sort(arr)
  }

}

class SelectionSort {

  def sort(a : Array[Ordered[Any]]) = {
    var N = a.length

    for (i <- 0 until N) {
        var min = i

        for(j <- i + 1 until N){
            if( less(a(j) , a(min))){
              min = j
            }
        exchange(a , i , min)
        }
    }

  }

  def less(v : Ordered[Any] , w : Ordered[Any]) = {
    v.compareTo(w) < 0
  }

  def exchange(a : Array[Ordered[Any]] , i : Integer , j : Integer) = {
    var swap : Ordered[Any] = a(i)
    a(i) = a(j)
    a(j) = swap
  }
}

【问题讨论】:

    标签: scala


    【解决方案1】:

    数组是不变的。即使AB 的子类型,您也不能将Array[A] 用作Array[B]。在这里查看原因:Why are Arrays invariant, but Lists covariant?

    Ordered 也不是,所以你的 less 实现也不起作用。

    您应该通过以下方式使您的实现通用:

    object SelectionSortTest {
    
      def main(args: Array[String]){
    
        val arr = Array("Hello","World")
    
        val ss = new SelectionSort()
        ss.sort(arr)
      }
    
    }
    
    class SelectionSort {
    
      def sort[T <% Ordered[T]](a : Array[T]) = {
        var N = a.length
    
        for (i <- 0 until N) {
            var min = i
    
            for(j <- i + 1 until N){
                if(a(j) < a(min)){ // call less directly on Ordered[T]
                  min = j
                }
            exchange(a , i , min)
            }
        }
    
      }
    
      def exchange[T](a : Array[T] , i : Integer , j : Integer) = {
        var swap = a(i)
        a(i) = a(j)
        a(j) = swap
      }
    }
    

    有点奇怪的声明T &lt;% Ordered[T] 的意思是“可以隐式转换为Ordered[T] 的任何类型T”。这确保您仍然可以使用小于运算符。

    查看详情: What are Scala context and view bounds?

    【讨论】:

    • 上述代码在 ss.sort(arr) 行导致编译时错误:“这一行的多个标记 - 推断的类型参数 [String] 不符合方法排序的类型参数边界 [T
    • 不,您需要保留T,因为Array[T] 是不变的。 Array[String] 不是 Array[Any]。需要更改的是[T &lt;: Ordered[T]][T &lt;% Ordered[T]]。这是一个视图绑定:stackoverflow.com/questions/4465948/… 必需,因为 String 不是(Scala-)有序的,因为它是 Java 类型而不是 Scala 类型,但可以转换为(Scala-)有序
    • 如果我将以下代码添加到排序方法的端到端:“ for(i
    • 错误不在于 Ordered,而在于 Sort。交换应该在j 循环之外(下一个括号)。但是请注意,如果您在输入中输入大写 C,它将在结果中排​​在第一位,因为排序只是通过 ascii 代码,并且所有大写字母都排在所有小写字母之前。
    【解决方案2】:

    @gzm0 的答案(带有一些非常好的链接)建议Ordered。我将补充一个涵盖 Ordering 的答案,它提供了等效的功能,而不会对您的课程造成太多影响。

    让我们调整排序方法以接受类型为“T”的数组,其中定义了 Ordering 隐式实例。

    def sort[T : Ordering](a: Array[T]) = {
      val ord = implicitly[Ordering[T]]
      import ord._ // now comparison operations such as '<' are available for 'T'
      // ...
      if (a(j) < a(min))
      // ...
    }
    

    [T : Ordering]implicitly[Ordering[T]] 组合等效于 Ordering[T] 类型的隐式参数:

    def sort[T](a: Array[T])(implicit ord: Ordering[T]) = {
      import ord._
      // ...
    }
    

    为什么这很有用? 想象一下,某个第三方向您提供了case class Account(balance: Int)。您现在可以像这样为其添加Ordering

    // somewhere in scope
    implicit val accountOrdering = new Ordering[Account] {
      def compare(x: Account, y: Account) = x.balance - y.balance
    }
    
    // or, more simply
    implicit val accountOrdering: Ordering[Account] = Ordering by (_.balance)
    

    只要该实例在范围内,您就应该能够使用sort(accounts)
    如果你想使用一些不同的排序,你也可以明确地提供它,像这样:sort(accounts)(otherOrdering)

    请注意,这与提供到 Ordering 的隐式转换没有太大区别(至少不在本问题的上下文中)。

    【讨论】:

      【解决方案3】:

      尽管在编写 Scala 时,我习惯于更喜欢函数式编程风格(通过组合器或递归)而不是命令式编程风格(通过变量和迭代),这一次,对于这个特定问题,老派的命令式嵌套循环会导致更简单、更高效的代码。

      对于某些类型的问题,我不认为回到命令式风格是错误的,例如排序算法通常会转换输入缓冲区(更像是一个过程),而不是产生一个新的排序集合。

      这是我的解决方案:

      package bitspoke.algo
      
      import scala.math.Ordered
      import scala.collection.mutable.Buffer
      
      abstract class Sorter[T <% Ordered[T]] {
      
        // algorithm provided by subclasses
        def sort(buffer : Buffer[T]) : Unit
      
        // check if the buffer is sorted
        def sorted(buffer : Buffer[T]) = buffer.isEmpty || buffer.view.zip(buffer.tail).forall { t => t._2 > t._1 }
      
        // swap elements in buffer
        def swap(buffer : Buffer[T], i:Int, j:Int) {
          val temp = buffer(i)
          buffer(i) = buffer(j)
          buffer(j) = temp
        }
      }
      
      
      class SelectionSorter[T <% Ordered[T]] extends Sorter[T] {
        def sort(buffer : Buffer[T]) : Unit = {
          for (i <- 0 until buffer.length) {
            var min = i
            for (j <- i until buffer.length) {
              if (buffer(j) < buffer(min))
                min = j
             }
             swap(buffer, i, min)
           }
        }
      }
      

      如您所见,为了实现参数多态,我更喜欢java.lang.Comparable,而不是使用scala.math.Ordered 和Scala View Bounds 而不是Upper Bounds。得益于 Scala 将原始类型隐式转换为 Rich Wrappers,这确实有效。

      您可以编写如下客户端程序:

      import bitspoke.algo._
      import scala.collection.mutable._
      
      val sorter = new SelectionSorter[Int]
      val buffer = ArrayBuffer(3, 0, 4, 2, 1)
      sorter.sort(buffer)
      
      assert(sorter.sorted(buffer))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-25
        • 1970-01-01
        • 1970-01-01
        • 2013-10-23
        相关资源
        最近更新 更多