我一直在想,为什么这段代码无法编译?
Scala中是否有一种方法可以创建通用参数化的方法/函数,并允许类似“ reduce”的操作。
此行为与类型擦除有什么共同之处吗?还是其他?我希望看到对此的广泛解释:)
def func2[B <: Int](data: Seq[B]): Unit = {
val operation = (a: B, b: B) => a.-(b)
data.reduce(operation)
}
编译器说:
type mismatch;
found : (B, B) => Int
required: (Int, Int) => Int
本着同样的精神-使用此方法在参数化集合上总体上可以调用任何“类似于流的”方法:
def func2[B <: Int](data: Seq[B]): Unit = {
val operation = (a: B, b: B) => a.-(b)
data.sum
}
还给出:
could not find implicit value for parameter num: Numeric[B]
答案 0 :(得分:2)
a.-(b)
的结果始终为Int
,而您的operation
函数为(B, B) => Int
。但是reduce
需要一个(B, B) => B
函数。
def reduce[A1 >: A](op: (A1, A1) => A1): A1
由于(Int, Int) => Int
的结果类型为Int
,因此operation
函数是编译器的唯一选择。
此变体编译:
def func2[B <: Int](data: Seq[B]): Unit = {
val operation = (a: Int, b: Int) => a.-(b)
data.reduce(operation)
}
Numeric
不是协变的。其界面为Numeric[T]
。 Hense Numeric[B]
不是Numeric[Int]
的{{1}}的子类,并且没有隐式的B <: Int
。
答案 1 :(得分:2)
为什么我不能对集合的类型设置上限,并假设B类型(具有该约束)只是我需要这些方法?
您的假设是正确的。您在B
的上限进行以下编译
val operation = (a: B, b: B) => a.-(b)
并且由于reduce
是协变的,因此也使Seq[B]
在Seq
上可用。
由于编译器知道“ B
ISA Int
”,因此存在-
方法。但是,它仍将返回Int
。因为+
的签名将返回类型限制为Int
def +(x: Int): Int
reduce
操作只能理解一种类型。因此,如果您有
reduce[B](operation)
它将期望operation
的类型为(B,B) => B
如果有的话
reduce[Int](operation)
它将期望operation
的类型为(Int,Int) => Int
可以做的一件事是
val operation = (a: Int, b: Int) => a - b
这很安全,因为您的B
始终也是Int
答案 2 :(得分:0)
this works
def func2[B](data: Seq[B], f: (B, B) => B): Unit = {
val operation = (a: B, b: B) => f(a, b)
data.reduce(operation)
}
答案 3 :(得分:0)
目前尚不清楚您要实现的目标。
首先限制B <: Int
毫无意义,因为Int
是Scala中的final
类。
第二,将reduce
与-
一起使用也没有意义,因为-
不是commutative。这很重要,因为reduce
与reduceLeft
/ reduceRight
或foldLeft
/ foldRight
不同,它不能保证评估的顺序。其实
def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(op)
与
是有效的默认实现def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceRight(op)
但是很明显,它们对于-
操作将产生不同的结果。
从更高的角度来看,看起来可以使用type classes尤其是Numeric
来完成与您想要实现的目标类似的事情。例如,您可以使用如下方法:
def product[B: Numeric](data: Seq[B]): B = {
val numeric = implicitly[Numeric[B]]
data.reduce(numeric.times)
}
请注意,乘法是可交换的,因此这是一个合理的实现。实际上,这几乎是在标准库中实现sum
和product
的方式。主要区别在于实际的实现使用foldLeft
,它允许为空的Seq
(分别为0和1)定义默认值