[英]Scala implicit conversions and parameters
I'm trying to solve an exercise from the book Scala by example , chapter 15 Implicit Parameters and Conver- sions which can be found here : 我正在尝试通过示例来解决《 Scala 》一书中的一个练习,该书的第15章隐式参数和转换可以在此处找到:
and have the following code sample : 并具有以下代码示例:
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
}
Unfortunately I don't find a way to have implicitly the converter assigned to the method sort so that the client code can look like : 不幸的是,我找不到一种隐式地将转换器分配给方法排序的方法,以使客户端代码看起来像:
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)
}
If I add the implicit keyword for the converter parameter of the methods merge and sort I get 如果我为方法合并和排序的转换器参数添加隐式关键字,则会得到
ambiguous implicit values 含糊的隐含值
compilation error message. 编译错误消息。
The type constraint you're placing on A
is called a context bound . 您对A
施加的类型约束称为上下文绑定 。 As you correctly write in your comment, A : OrderedView
means that there is available an implicit value of A[OrderedView]
. 当您在注释中正确书写时, A : OrderedView
意味着存在隐式值A[OrderedView]
。 Your mistake is how you try to get that instance. 您的错误是如何尝试获取该实例。 You wrote the method signature as: 您将方法签名写为:
def merge[A: OrderedView](xs: List[A], ys: List[A])(c: OrderedView[A]): List[A]
But when using a context bound, it should be: 但是,当使用上下文绑定时,它应该是:
def merge[A: OrderedView](xs: List[A], ys: List[A]): List[A]
And then use the implicitly
operator to get the instance you want. 然后使用implicitly
运算符获取所需的实例。 So your methods could look like: 因此,您的方法可能类似于:
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]]
...
}
Your other option is to get rid of the context view and use an implicit parameter instead (and then don't use implicitly
in the method body): 您的另一个选择是摆脱上下文视图,而改用隐式参数(然后在方法主体中不要implicitly
使用):
def merge[A](xs: List[A], ys: List[A])(implicit c: OrderedView[A]): List[A]
These are exactly equivalent. 这些是完全等效的。 In fact, the context bound is basically translated into this version during compilation. 实际上,上下文绑定在编译过程中基本上已转换为该版本。 What you had was mixing these idioms, by using a context bound and using another parameter list (although yours was not implicit, which is required). 通过使用上下文绑定和使用另一个参数列表(尽管您不是隐式的,这是必需的),您正在混合这些习惯用法。
In your example, you don't even need an explicit instance of OrderedView[A]
. 在您的示例中,您甚至都不需要显式的OrderedView[A]
实例。 Since it is available in implicit scope (as guaranteed by the context bound), it is automatically applied when necessary. 由于它在隐式范围内可用(由上下文绑定保证),因此在必要时会自动应用它。 So, you can even simplify things further: 因此,您甚至可以进一步简化事情:
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)
Also, you could use a view bound here, instead of the context bound. 另外,您可以在此处使用视图绑定 ,而不是上下文绑定。 Then, you don't even need to introduce the OrderedView
type alias: 然后,您甚至不需要引入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]
Read more about context bounds (and view bounds) here . 在此处阅读有关上下文范围(和视图范围)的更多信息 。
As an unrelated note, you should also explore using match
statements, which are pretty powerful in Scala: 作为不相关的说明,您还应该探索使用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)
}
This definition: 这个定义:
def sort[A: OrderedView](xs: List[A])(c: OrderedView[A]): List[A]
is in fact equivalent to this: 实际上等于:
def sort[A](xs: List[A])(c: OrderedView[A])(implicit ev: OrderedView[A]): List[A]
That is, your c: OrderedView[A]
parameter is absolutely independent on the context bound, it is just another parameter. 也就是说,您的c: OrderedView[A]
参数绝对独立于上下文绑定,它只是另一个参数。
You just need either to omit the context bound: 您只需要忽略上下文绑定:
def sort[A](xs: List[A])(implicit c: OrderedView[A]): List[A]
or omit the parameter: 或省略参数:
def sort[A: OrderedView](xs: List[A]): List[A]
In the latter case, however, if you want to access the implicit parameter you will have to do it with implicitly
: 但是,在后一种情况下,如果要访问隐式参数,则必须使用implicitly
:
implicitly[OrderedView[A]](xs.head)
Maybe you won't even need to call it explicitly as an implicit conversion will fire automatically. 也许您甚至不需要显式调用它,因为隐式转换将自动触发。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.