0

我正在学习和试验 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
}

如果是这样,如何指定给定的类型必须是 Numeric ?我尝试用 B 替换 B : Numeric 但仍然给出相同的错误

4

2 回答 2

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作为参数的,因为我们在defaultParamFuncbody 中实例化了函数,所以引用没有问题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方法。磁铁图案来救援!

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)(_ * _)      
于 2014-04-19T14:01:54.573 回答
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"))

编辑:实际上,ScalaNumeric已经为您做到了:

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
}
于 2014-04-18T20:29:55.860 回答