2013-06-19 77 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)'也可以 –