Scala中参数中函数的默认值

Scala中参数中函数的默认值,scala,generics,default-parameters,Scala,Generics,Default Parameters,我在学习和实验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 } 这没有给出任何错误 但当我尝试

我在学习和实验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,但仍然给出相同的错误

错误是因为编译器不知道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
}

代码的第一个问题是一个小的遗漏:您需要导入
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
位于最后一个参数列表中,因此无法在默认值中使用它。 真倒霉

好的,让我们试着智取编译器并将方法一分为二,这样我们就可以在参数
f
之前有隐式参数
num

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
body中实例化了函数,所以引用
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)()
当然,对于这样一个微小的结果,这有点做作。但它确实有效:

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)(_ * _)      
scala>defaultParamFunc(2,3)(*.)
:18:错误:缺少扩展函数的参数类型((x$1,x$2)=>x$1.$times(x$2))
defaultParamFunc(2,3)(*.uu)

这不可编译(请参阅我的答案),谢谢您的回复。第一个示例有效,但使用数字代码,我在defaultParamFunc函数中遇到错误“找不到参数e:numeric[B]的隐式值”。add函数起作用了。我试过你的密码。它不是编译。当我使用defaultParamFunc(2,3)()时,它会给出错误“未指定值参数”。当我尝试defaultParamFunc(2,3){(x:Int,y:Int)=>x*y}时,我得到了错误“found:(Int,Int)=>Int required:test.ScalaTest.MyMagnet[Int]注意:隐式方法fromUnit在这里不适用,因为它位于应用程序点之后,并且它缺少一个显式的结果类型“它确实可以编译”(在REPL,scala版本2.10.1中测试)。在REPL中,您只需注意将
MyMagnet
特征和
MyMagnet
对象一起评估,否则后者将不会被视为前者的伴生对象。但是,我可以从错误消息中看到,您没有使用REPL。我所能建议的是,您发布准确的代码(如“整个源文件”),这样我就可以看到哪里出了问题。对不起。我是斯卡拉的新手。我没有使用REPL。我正在使用SBT运行它。我的scala版本是2.10.3。这是文件:好的。。现在它开始工作了。。愚蠢的错误。。非常感谢您的详细解释。这是非常有益的。:)
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)(_ * _)