2013-06-19 30 views
6

別の関数gを取る関数fを定義したいと思います。我々はgnダブル(いくつかの固定のためn)を取ると、ダブルを返す必要があります。関数呼び出しf(g)は、特定の値nを返します。例えば入力が変数arityの関数である汎用スカラ関数

f(Math.max) = 2 Math.sinはDouble => Double型を持つのでMath.sinは(Double, Double) => Double、及びf(Math.sin) = 1を入力しているので。

Scalaのジェネリックスを使用してfを定義するにはどうすればよいですか?

私は成功しなかったいくつかのフォームを試しました。例えば:私たちはコンパイル時にnの値を抽出することはできません、とだけDouble値を含むようにAを制約することはできませんので、

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

これは動作しません。

+0

あなたは、 'g'はnタプルを取ると言っていますが、あなたの例である' Math.max'はタプルを取る 'Function1'ではなく、n項関数です。あなたはそれを明確にすべきです。 –

+0

固定式、良好なキャッチ – tba

答えて

6

Sprayチームによって作成されたMagnet Patternというパターンがあります。それはおそらく最も簡単な解決策は、(あなたは/関数の引数をチェックし消去を入力するため、実行時に型を返すことができません

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 _)); 

としてオーバーロードを使用することですあなたが

2

欲しいものexectlyないので、私はあなたができると信じて「tはあなたの要件を満たすだろう完全に汎用的な関数を作成します。)

4

をこれは、私は常にいくつかの時点でやってみたかった何か私はShapelessに見するための良い口実だった:)

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

(1)

シェイプレスは、アリティに対していくつかの抽象化、特に異種リスト(HList)としての表現を提供します。任意のアリティの関数は、FnHList(引数としてHListをとる関数)と見ることができます。

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型クラスは、引数の上限を目撃することができます

$ 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 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)

ここでも何らかの形でアリティを抽出する必要があります。タイプレベルでは、これはのために提供されるLengthです。ランタイム値を取得するには、別のタイプのクラス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() 

テスト:残念ながら、多くのmath操作が過負荷、および強力な型制約なしている

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 

注、ScalaはあなたにDoubleを与えることはありませんバージョンは自動的に使用されますが、何らかの理由でIntバージョンが使用されます:

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

この作業を行うには、間接指示math.max _: ((Double, Double) => Double)が必要です。


具体的なケースでこれを行うのが最良の方法だとは言えませんが、私はそれが楽しい探索だったと思います。

+0

P. 'math.max(_:Double、_:Double)'もやります –