【问题标题】:Scala implicit conversions and parametersScala 隐式转换和参数
【发布时间】:2015-04-08 04:26:29
【问题描述】:

我正在尝试解决本书 Scala by example 中的一个练习,第 15 章隐式参数和转换- 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)
  }

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

模糊的隐含值

编译错误信息。

【问题讨论】:

    标签: scala implicit-conversion


    【解决方案1】:

    您对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]
    

    这些是完全等价的。其实context bound基本上是在编译的时候翻译成这个版本的。你所拥有的是混合这些习语,通过使用上下文绑定使用另一个参数列表(尽管你的不是隐式的,这是必需的)。

    在您的示例中,您甚至不需要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)
    }
    

    【讨论】:

    • implicitly 在这里不是必需的。 c 的所有使用都可以简单地删除,即else if (xs.head &lt; ys.head) xs.head :: merge(xs.tail, ys)
    【解决方案2】:

    这个定义:

    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)
    

    也许您甚至不需要显式调用它,因为隐式转换会自动触发。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-27
      • 1970-01-01
      • 2011-08-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多