我正在学习和试验Scala。我想实现一个泛型类型的函数,它将函数作为参数并提供该函数的默认实现。
现在当我尝试没有泛型类型时,它可以工作:
def defaultParamFunc(z: Int, y: Int)(f: (Int, Int) => Int = (v1: Int,v2: Int) => { v1 + v2 }) : Int = {
val ans = f(z,y)
println("ans : " + ans)
ans
}
这不会给出任何错误
但是当我尝试使用泛型类型时,
def defaultParamFunc[B](z: B, y: B)(f: (B, B) => B = (v1: B,v2: B) => { v1 + v2 }) = {
val ans = f(z,y)
println("ans : " + ans)
ans
}
我收到错误:
[error] found : B
[error] required: String
[error] def defaultParamFunc[B](z: B, y: B)(f: (B, B) => B = (v1: B,v2: B) => { v1 + v2 }) = {
[error] ^
错误是因为编译器不知道B类型是否可以添加?因为当我只返回v1或v2而不是v1 + v2时,它可以工作..
def defaultParamFunc[B](z: B, y: B)(f: (B, B) => B = (v1: B,v2: B) => { v1 }) = {
val ans = f(z,y)
println("ans : " + ans)
ans
}
如果是,如何指定给定的类型必须是数字?我尝试用B:Numeric替换B但仍然给出了相同的错误
答案 0 :(得分:2)
您的代码的第一个问题是遗漏:您需要导入Numeric
实例的成员,以便将+
纳入范围(这实际上会带来num.mkNumericOps
进入范围,启用+
方法:
我们试试这个:
def defaultParamFunc[B](z: B, y: B)(f: (B, B) => B = (v1: B,v2: B) => { v1 + v2 })(implicit num: Numeric[B]) = {
// Brings `+` into scope.
// Note that you can also just import Numeric.Implicits._ for the same effect
import num._
val ans = f(z,y)
println("ans : " + ans)
ans
}
不幸的是,这仍然无法编译。这里的问题是参数的默认值只能引用早期参数列表中的参数。
由于num
位于最后一个参数列表中,因此无法在默认值中使用它。
好运。
好的,让我们试着超越编译器并将方法分成两部分,这样我们就可以在参数num
之前得到隐式参数f
:
def defaultParamFunc[B](z: B, y: B)(implicit num: Numeric[B]) = new {
// NOTE: using structural typing here is rather inefficient.
// Let's ignore that for now.
import num._
def apply(f: (B, B) => B = (v1: B, v2: B) => { v1 + v2 }) = {
val ans = f(z,y)
println("ans : " + ans)
ans
}
}
很好,它汇编了。我们在这里做的是defaultParamFunc
实际上返回一个(伪)函数实例。此函数以f
为参数,
并且因为我们在defaultParamFunc
正文中实例化了函数,所以引用num
没有问题。
但是,我们不要太快庆幸。如果我们尝试调用它而不指定f
参数,编译器就不满意了:
scala> defaultParamFunc(5, 7)()
<console>:17: error: not enough arguments for method defaultParamFunc: (implicit num: Numeric[Int])((Int, Int) => Int) => Int{def apply$default$1: (Int, Int) => Int}.
Unspecified value parameter num.
defaultParamFunc(5, 7)()
糟糕。编译器认为空参数列表用于defaultParamFunc
中的第二个(隐式)参数列表,而不是(伪)函数实例的参数列表。
我们必须找到另一种不需要defaultParamFunc
方法中的隐式参数的方法。 Magnet patterns救援!
trait MyMagnet[B] extends ((B,B) => B)
object MyMagnet {
implicit def fromFunc[B]( f: (B, B) => B ) = new MyMagnet[B] {
def apply(z: B, y: B): B = {
val ans = f(z,y)
println("ans : " + ans)
ans
}
}
implicit def fromUnit[B](unit: Unit)(implicit num: Numeric[B]) = new MyMagnet[B]{
import num._
def apply(v1: B, v2: B): B = { v1 + v2 }
}
}
def defaultParamFunc[B](z: B, y: B)(magnet: MyMagnet[B]): B = magnet(z, y)
当然,对于如此微小的结果,这有点做作。但它确实有效:
scala> defaultParamFunc(2,3)()
res15: Int = 5
scala> defaultParamFunc(2,3){(x:Int, y:Int) => x*y }
ans : 6
res16: Int = 6
请注意,通过使用磁铁模式,我们已经失去了类型推断的好处。所以我们不能只做以下事情:
scala> defaultParamFunc(2,3)(_ * _)
<console>:18: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$times(x$2))
defaultParamFunc(2,3)(_ * _)
答案 1 :(得分:0)
错误是因为编译器不知道B类型是否可以添加?
是。类型参数B
在您的示例中没有约束 - 您可以在使用此函数时使用任何类型作为参数。编译器无法提前检查您使用的任何类型都有+
方法。
你可以使用类型类方法,这里有一些东西可以给你一个想法:
// This defines a generic 'plus' method
trait Plus[T] {
def plus(x: T, y: T): T
}
// This is a generic method that can do 'plus' on whatever type
// for which there is an implicit Plus value in scope
def add[T : Plus](a: T, b: T) = implicitly[Plus[T]].plus(a, b)
// This defines what 'plus' means for Ints
implicit val intPlus = new Plus[Int] {
def plus(x: Int, y: Int): Int = x + y
}
// This defines what 'plus' means for Strings
implicit val stringPlus = new Plus[String] {
def plus(x: String, y: String): String = x.concat(y)
}
// Examples of use
println(add(2, 3))
println(add("hello", "world"))
编辑:实际上,Scala的Numeric
已经为你做了这个:
def add[T : Numeric](x: T, y: T) = implicitly[Numeric[T]].plus(x, y)
// Use it with integers
println(add(2, 3))
// Or doubles
println(add(1.5, 2.4))
// Or with for example BigDecimal
println(add(BigDecimal("1.234"), BigDecimal("4.567")))
所以你应该可以这样做:
def defaultParamFunc[B : Numeric](z: B, y: B)(f: (B, B) => B = (v1: B,v2: B) => { implicitly[Numeric[B]].plus(v1, v2) }) = {
val ans = f(z,y)
println("ans : " + ans)
ans
}