2013-06-19 56 views
6

我想定义一个函数f,它需要另一个函数g。我们要求g采取n双打(对于一些固定n)并返回一个双。函数调用f(g)应返回具体值n通用Scala函数,其输入是变量函数的函数

例如,因为Math.sin的类型为(Double, Double) => Double,f(Math.sin) = 1,因为Math.sin的类型为Double => Double

如何使用Scala泛型定义f

我试过了几种没有成功的表格。例如:

def f[A <: Product](g: Product => Double) = {...} 

这行不通,因为我们不能提取的n在编译时的值,而不能约束A只包含Double值。

+0

你说'g'需要一个n元组,但是你的例子'Math.max'是一个n元函数,而不是'Function1',它需要一个元组。你应该澄清一点。 –

+0

固定,良好的渔获 – tba

回答

2

也许最简单的解决方法是使用重载的

def f(g:() => Double) = 0; 
def f(g: (Double) => Double) = 1; 
def f(g: (Double, Double) => Double) = 2; 
def f(g: (Double, Double, Double) => Double) = 2; 
// ... 

println(f(Math.pow _)); 
println(f(Math.sin _)); 

(您不能检查函数参数/在由于类型擦除运行时间返回类型,所以我相信你可以“T创建一个能够满足您的需求完全通用的功能。)

4

这是一个很好的借口,我寻找到Shapeless,这是我一直想在某些时候做:)

$ git clone [email protected]:milessabin/shapeless.git 
... 
$ cd shapeless 

(1)

无形状提供了对arity的一些抽象,特别是表示为异构列表(HList)。任意元素的函数可以看作FnHList(以HList作为参数的函数)。

$ sbt shapeless-core/console 
scala> import shapeless._ 
import shapeless._ 

scala> def isFunction[A](fun: A)(implicit fnh: FnHLister[A]) {} 
isFunction: [A](fun: A)(implicit fnh: shapeless.FnHLister[A])Unit 

scala> isFunction(math.sqrt _) 

scala> isFunction(math.random _) 

(2)

现在,让我们要求该函数返回一个Double

scala> def isFunReturningDouble[A](fun: A)(implicit fnh: FnHLister[A] { type Result = Double }) {} 
isFunReturningDouble: [A](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double})Unit 

scala> isFunReturningDouble(math.sqrt _) 

scala> isFunReturningDouble(math.signum _) 
<console>:12: error: could not find implicit value for parameter fnh: shapeless.FnHLister[Int => Int]{type Result = Double} 
       isFunReturningDouble(math.signum _) 
           ^

(3)

LUBConstraint类型的类可以见证上限的说法列表:

scala> def isValidFun[A, B <: HList](fun: A)(implicit fnh: FnHLister[A] { type Result = Double; type Args = B }, lub: LUBConstraint[B, Double]) {} 
isValidFun: [A, B <: shapeless.HList](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double; type Args = B}, implicit lub: shapeless.LUBConstraint[B,Double])Unit 

scala> isValidFun(math.random _) 

scala> isValidFun((i: Int) => i.toDouble) 
<console>:12: error: could not find implicit value for parameter lub: shapeless.LUBConstraint[B,Double] 
       isValidFun((i: Int) => i.toDouble) 
         ^

(4)

现在我们仍然需要以某种方式提取arity。在类型级别上,这将是Length,它是为HList提供的。要获得运行时值,需要另一个类型ToInt

这里是最后的功能:

import shapeless._ 

def doubleFunArity[A, B <: HList, C <: Nat](fun: A)(implicit 
    fnh: FnHLister[A] { type Result = Double; type Args = B }, 
    lub: LUBConstraint[B, Double], 
    len: Length[B] { type Out = C }, 
    res: ToInt[C] 
): Int = res() 

测试:

scala> doubleFunArity(math.sqrt _) 
res15: Int = 1 

scala> doubleFunArity(math.random _) 
res16: Int = 0 

scala> val g: (Double, Double) => Double = math.max _ 
g: (Double, Double) => Double = <function2> 

scala> doubleFunArity(g) 
res17: Int = 2 

注意,遗憾的是很多math操作过载,并没有强大的类型约束,斯卡拉不会给你的Double版本,但由于某种原因将使用Int版本:

scala> math.max _ 
res18: (Int, Int) => Int = <function2> 

所以我需要间接的math.max _: ((Double, Double) => Double)来完成这项工作。


不是说这是在具体情况下做到这一点的最佳方式,但我认为这是一个有趣的探索。

+0

P.S. 'math.max(_:Double,_:Double)'也可以 –