如何调用合并排序

时间:2014-05-03 22:19:59

标签: scala

以下代码基于Merge sort from "Programming Scala" causes stack overflow

  def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T] = {

    def merge(xs: List[T], ys: List[T], acc: List[T]): List[T] =
      (xs, ys) match {
        case (Nil, _) => ys.reverse ::: acc
        case (_, Nil) => xs.reverse ::: acc
        case (x :: xs1, y :: ys1) =>
          if (less(x, y)) merge(xs1, ys, x :: acc)
          else merge(xs, ys1, y :: acc)
      }
    val n = xs.length / 2
    if (n == 0) xs
    else {
      val (ys, zs) = xs splitAt n
      merge(msort(less)(ys), msort(less)(zs), Nil).reverse
    }

  }

当我尝试使用:

调用msort时
  val l = List(5, 2, 4, 6, 1, 3)
  msort[Int](l)

我收到错误:

Multiple markers at this line - type mismatch; found : List[Int] required: (Int, Int) => Boolean - type mismatch; 
 found : List[Int] required: (Int, Int) => Boolean - missing arguments for method msort in object mergesort; follow 
 this method with `_' if you want to treat it as a partially applied function

如何调用msort&为什么函数需要作为调用的一部分?

2 个答案:

答案 0 :(得分:4)

在Scala中,可以Multiple Parameters Lists。您的调用只传递一个参数。

该方法声明为def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T],因此第一个参数的类型为(T, T) => Boolean,该函数采用类型T的两个参数并返回Boolean值。你传递了List[Int],这让Scala抱怨。

你为什么要这样做,你可能会问。好吧,请考虑以下示例。

val stringSorter = msort[String]((a, b) => a.compareTo(b) < 0) _
// stringSorter: List[String] => List[String] = <function1>

val integerSorter = msort[Int]((a, b) => a < b) _
// integerSorter: List[Int] => List[Int] = <function1>

这两个调用创建了两个只接受一个参数的新函数 - 您要排序的列表。您不必告诉它如何比较元素,因为您已经这样做了。请注意,您可以使用不同的列表作为参数调用相同的函数。

integerSorter(List(2, 3, 1))
// res0: List[Int] = List(1, 2, 3)

integerSorter(List(2, 4, 1))
// res1: List[Int] = List(1, 2, 4)

stringSorter(List("b", "a", "c"))
res3: List[String] = List(a, b, c)

另请注意,新创建的函数是类型安全的,以下代码将失败:

integerSorter(List("b", "a", "c"))
<console>:10: error: type mismatch;
 found   : String("b")
 required: Int
              integerSorter(List("b", "a", "c"))

隐式参数

正如链接中的文章所提到的,您可能想要使用多个参数列表的原因之一是隐式参数。

  

使用隐式参数时,您使用隐式关键字   适用于整个参数列表。因此,如果你只想要一些   要隐式的参数,必须使用多个参数列表。

让我们修改你给我们一些介绍新类型的示例代码:

trait Comparator[T] {
  def less(a: T, b: T): Boolean
}

然后让我们交换参数列表,并将implicit关键字添加到第二个关键字,现在它变为:

def msort[T](xs: List[T])(implicit c: Comparator[T]): List[T] = {

    def merge(xs: List[T], ys: List[T], acc: List[T]): List[T] =
      (xs, ys) match {
        case (Nil, _) => ys.reverse ::: acc
        case (_, Nil) => xs.reverse ::: acc
        case (x :: xs1, y :: ys1) =>
          if (c.less(x, y)) merge(xs1, ys, x :: acc)
          else merge(xs, ys1, y :: acc)
      }
    val n = xs.length / 2
    if (n == 0) xs
    else {
      val (ys, zs) = xs splitAt n
      merge(msort(ys)(c), msort(zs)(c), Nil).reverse
    }

}

现在你可以声明一个隐含的对象,这个对象将在你不提供时使用,例如

implicit val intComparator = new Comparator[Int] { def less(a: Int, b: Int) = a < b }

msort(List(5, 3, 1, 3))
// res8: List[Int] = List(1, 3, 3, 5)

虽然这看起来似乎不太吸引人,但在设计API时会给你额外的灵活性。我们假设我们有一个名为CustomType的类型。它可以在随播对象中声明隐式,它将由编译器“自动”解析。

case class CustomType(ordinal: Int, name: String)

object CustomType {
  implicit val customTypeComparator = new Comparator[CustomType] { 
    def less(a: CustomType, b: CustomType) = a.ordinal < b.ordinal 
  }
}


msort(List(CustomType(2, "Second"), CustomType(1, "First")))
// res11: List[CustomType] = List(CustomType(1,First), CustomType(2,Second))

答案 1 :(得分:1)

def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T]

此函数有两个参数:函数less和列表xs

  

如何调用msort?

您必须为两个参数提供值:msort(...)(...)

  

为什么函数需要作为调用的一部分?

因为参数less是用函数类型(T, T) => Boolean声明的。