Scala 隐式转换和参数
Scala implicit conversions and parameters
我正在尝试解决 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 关键字,我得到
ambiguous implicit values
编译错误信息。
这个定义:
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)
也许您甚至不需要显式调用它,因为隐式转换会自动触发。
您在 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)
}
我正在尝试解决 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 关键字,我得到
ambiguous implicit values
编译错误信息。
这个定义:
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)
也许您甚至不需要显式调用它,因为隐式转换会自动触发。
您在 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)
}