2014-04-18 55 views
0

我正在学习和尝试斯卡拉。我想实行一个泛型类型,这需要一个函数作为参数,并提供该功能的默认实现的功能..斯卡拉参数中函数的默认值

现在,当我尝试没有泛型类型,它的工作原理:

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替换B:数字但仍然给出相同的错误

回答

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位于最后一个参数列表中,因此无法将其用于默认值。 运气不好。

好了,让我们尝试智取编译器和一分为二的方法,这样我们就可以在参数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身上实例化功能没有任何问题参考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)(_ * _)  
+0

好的。我试过你的代码。这不是编译。当我使用defaultParamFunc(2,3)()它给出了错误'未指定值参数磁铁'。当我尝试defaultParamFunc(2,3){(x:Int,y:Int)=> x * y},我得到了错误'found:(Int,Int)=> Int required:test.ScalaTest.MyMagnet [ Int] 注意:来自Unit的隐式方法在这里是不适用的,因为它在应用程序点之后,它缺少一个明确的结果类型' –

+0

它**不会**编译(在REPL中测试,scala版本2.10.1)。在REPL中,你只需要小心评估'MyMagnet' *特征*和'MyMagnet' *对象*,否则后者不会被视为前者的伴侣对象。但是我可以从错误消息中看到您没有使用REPL。我可以建议的是,你发布的确切代码(如“整个源文件”),所以我可以看到什么是错的。 –

+0

对不起。我是斯卡拉新手。我没有使用REPL。我正在使用SBT运行它。我的scala版本是2.10.3。这里是文件:http://pastebin.com/HDgprLZD –

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 
} 
+0

这不会编译(请参阅我的回答) –

+0

感谢您的回应。第一个例子工作,但使用数字代码,我得到了错误 '找不到默认的参数e:Numeric [B]在defaultParamFunc函数中的值。添加功能工作 –