我正在阅读Manning的Scala功能编程,由Paul Chiusano和Runar Bjarnason撰写。在第3章中,有一个用于创建List的代码,并且有用于实现列表的各种方法的赋值。以下是我的List
的部分实现package src.Cons
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
object List {
//my issue is I do not want to pass a list to sum but want to use objectName.sum notation
def sum(ints:List[Int]):Int = ints match {
case Nil => 0
case Cons(x,xs) => x+sum(xs)
}
}
问题 - 如何创建列表,以便我可以拨打l.sum
而不是List.sum(l)
?
答案 0 :(得分:3)
您可以" PmL",正如@Gabriele Petronella建议的那样,或者您可以将sum()
方法移动到Cons
类,如@DeadNight所写,但在之前这些可以解决您List
对象与List
特征之间当前的冲突。
sum()
对象中的List
只能加List[Int]
,但您的类定义使用更通用的类型成员,因此您无法使用{{ 1}}因为编译器不知道如何添加两个+
类型。
如果您想将A
限制为仅处理数字类型,那么这将有效。
List
答案 1 :(得分:3)
sum
成员问题是,您不知道如何对每种类型List[A]
A
求助,只有List[Int]
。如果在A
为Int
...
让我们来看看标准库。我们对Option#flatten method感兴趣,因为:
val o1 = Option(Option(3)).flatten // compiles
val o2 = Option(4).flatten // does not compile
注意奇怪的implicit ev: <:<[A, Option[B]]
。这是关键 - 它是编译器为您提供的东西,但只有在编译时知道,Option[A]
是某种类型Option[Option[B]]
的子类型B
}}。这是我们可以使用的技巧。
sealed trait List[+A] {
def sum(implicit ev: A <:< Int): Int = this match {
case Nil => 0
case Cons(x, xs) => x + xs.sum // <- here x is magically converted to Int, so we can use plus
}
}
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
println(Cons(4, Cons(38, Nil)).sum) // 42
请注意,您可以将<:<[A, B]
写为A <:< B
。
注意:还有=:=[A, B]
类型,因为当您A
正好Int
时 - 您可以使用其中任何一种
实际上,std库有sum
方法,它的类型甚至更奇怪:
def sum(implicit ev: Numeric[A])
。这样做允许它处理任何类似数字的类型,如Double
和Int
,并具有比较,减法,乘法等操作。因此,您可以使它更通用。我建议你在阅读了关于Monoids的章节之后再这样做了。)
答案 2 :(得分:1)
您可以使用所谓的"Pimp my Library"模式。
定义隐式类ListOps
implicit class ListOps[+A](list: List[A]) {
def sum = List.sum(this)
}
现在您可以致电list.sum
。隐式转换将被触发,编译器将其解释为ListOps(list).sum
。
答案 3 :(得分:0)
您可以将具体定义留给Nil&amp;缺点
package src.Cons
sealed trait List[+A] {
def sum: Int
}
case object Nil extends List[Nothing] {
val sum: Int = 0
}
case class Cons[+A](h:A, t:List[A]) extends List[A] {
def sum: Int = h + t.sum
}