[英]Scala implicit conversions and parameters
我正在嘗試通過示例來解決《 Scala 》一書中的一個練習,該書的第15章隱式參數和轉換可以在此處找到:
並具有以下代碼示例:
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)
}
如果我為方法合並和排序的轉換器參數添加隱式關鍵字,則會得到
含糊的隱含值
編譯錯誤消息。
您對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]
作為不相關的說明,您還應該探索使用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)
}
這個定義:
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)
也許您甚至不需要顯式調用它,因為隱式轉換將自動觸發。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.