Scala隐式转换和参数

时间:2015-04-07 17:48:39

标签: scala implicit-conversion

我正在尝试从书中Scala by example第15章隐式参数和Conver-解决练习 sions 可以在这里找到:

并拥有以下代码示例:

object DemoImplicitConversions {
  def main(args: Array[String]) {
    val xs = List(new Num(1), new Num(4), new Num(2), new Num(6),new Num(3))
    val sortedXs = sort(xs)(num2ordered)
    print(sortedXs.mkString(","))
  }

  type OrderedView[A] = A => Ordered[A]

  // View bound : [A <% Ordered[A]] - means that sort is applicable to lists of type A such that there exists an
  // implicit conversion from A to Ordered[A]

  def sort[A: OrderedView](xs: List[A])(c: OrderedView[A]): List[A] =
    if (xs.isEmpty || xs.tail.isEmpty) xs
    else {
      val (ys, zs) = xs.splitAt(xs.length / 2)
      merge(ys, zs)(c)
    }

  def merge[A: OrderedView](xs: List[A], ys: List[A])(c: OrderedView[A]): List[A] =
    if (xs.isEmpty) ys
    else if (ys.isEmpty) xs
    else if (c(xs.head) < ys.head) xs.head :: merge(xs.tail, ys)(c)
    else ys.head :: merge(xs, ys.tail)(c)

  implicit def num2ordered(x: Num): Ordered[Num] = new Ordered[Num] {
    override def compare(y: Num): Int =
      if (x.value < y.value) -1
      else if (x.value > y.value) 1
      else 0

  }
}

case class Num(value: Int)  {
  override def toString: String = value.toString
}

不幸的是,我找不到一种方法来隐式地将转换器分配给方法排序,以便客户端代码看起来像:

def main(args: Array[String]) {
    val xs = List(new Num(1), new Num(4), new Num(2), new Num(6),new Num(3))
    val sortedXs = sort(xs)
    printList(sortedXs)
  }

如果我为方法合并和排序的转换器参数添加隐式关键字,我得

  

含糊不清的隐含值

编译错误消息。

2 个答案:

答案 0 :(得分:4)

您放在A上的类型约束称为上下文绑定。正确地在评论中写到,A : OrderedView表示可用隐含值A[OrderedView]。你的错误就是如何尝试获得该实例。您将方法签名编写为:

def merge[A: OrderedView](xs: List[A], ys: List[A])(c: OrderedView[A]): List[A]

但是当使用上下文绑定时,它应该是:

def merge[A: OrderedView](xs: List[A], ys: List[A]): List[A]

然后使用implicitly运算符获取所需的实例。所以你的方法看起来像:

def sort[A: OrderedView](xs: List[A]): List[A] = (merge _).tupled(xs.splitAt(xs.length / 2))

def merge[A: OrderedView](xs: List[A], ys: List[A]): List[A] = {
  //This is how we get the c instance      
  val c = implicitly[OrderedView[A]] 
  ...
}

您的另一个选择是删除上下文视图并使用隐式参数(然后不要在方法体中使用implicitly):

def merge[A](xs: List[A], ys: List[A])(implicit c: OrderedView[A]): List[A]

这些完全相同。实际上,在编译期间,上下文绑定基本上已翻译成此版本。你有什么混合这些习语,使用上下文绑定使用另一个参数列表(虽然你的是非隐式的,这是必需的)。

在您的示例中,您甚至不需要OrderedView[A]的显式实例。由于它在隐式范围内可用(由上下文绑定保证),因此在必要时会自动应用它。所以,你甚至可以进一步简化:

def merge[A: OrderedView](xs: List[A], ys: List[A]): List[A] =
    if (xs.isEmpty) ys
    else if (ys.isEmpty) xs
    //Here, the implicit conversion occurs on xs.head
    else if (xs.head < ys.head) xs.head :: merge(xs.tail, ys)
    else ys.head :: merge(xs, ys.tail)

此外,您可以在此处使用视图绑定,而不是上下文绑定。然后,您甚至不需要引入OrderedView类型别名:

def sort[A <% Ordered[A]](xs: List[A]): List[A]
def merge[A <% Ordered[A]](xs: List[A], ys: List[A]): List[A]
//Or, equivalently:
def sort[A](xs: List[A])(implicit ev: A => Ordered[A]): List[A]
def merge[A](xs: List[A], ys: List[A])(implicit ev: A => Ordered[A]): List[A]

阅读有关上下文边界(和视图边界)here的更多信息。

作为一个不相关的注释,您还应该使用match语句进行探索,这些语句在Scala中非常强大:

def merge[A <% Ordered[A]](xs: List[A], ys: List[A]): List[A] = (xs, ys) match {
      case (Nil, _) => ys
      case (_, Nil) => xs
      case (xhd::xtl, yhd::ytl) if xhd < yhd => xhd :: merge(xtl, ys)
      case (_, yhd::ytl) => yhd :: merge(xs, ytl)
}

答案 1 :(得分:1)

这个定义:

def sort[A: OrderedView](xs: List[A])(c: OrderedView[A]): List[A]

实际上相当于:

def sort[A](xs: List[A])(c: OrderedView[A])(implicit ev: OrderedView[A]): List[A]

也就是说,您的c: OrderedView[A]参数与上下文绑定完全无关,它只是另一个参数。

您只需要省略上下文绑定:

def sort[A](xs: List[A])(implicit c: OrderedView[A]): List[A]

或省略参数:

def sort[A: OrderedView](xs: List[A]): List[A]

但是,在后一种情况下,如果要访问隐式参数,则必须使用implicitly

implicitly[OrderedView[A]](xs.head)

也许你甚至不需要明确地调用它,因为隐式转换会自动触发。