7

f別の関数を取る関数を定義したいggDoubleを取りn(いくつかの固定されたn)、Double を返す必要があります。関数呼び出しf(g)は の特定の値を返す必要がありnます。

たとえば、f(Math.max) = 2Math.sin には type(Double, Double) => Doubleがありf(Math.sin) = 1、Math.sin には type があるためDouble => Doubleです。

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

成功せずにいくつかのフォームを試しました。例えば:

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

nコンパイル時にの値を抽出できず、値Aのみを含むように を制約できないため、これは機能しませんDouble

4

3 に答える 3

7

スプレーチームが作成したマグネットパターンと呼ばれるパターンがあります。それはまさにあなたが望むことをします

于 2013-06-19T08:47:14.753 に答える
6

これは、私が常にある時点でやりたいと思っていたShapelessを調べるための良い言い訳でした:)

$ git clone git@github.com:milessabin/shapeless.git
...
$ cd shapeless

(1)

Shapeless は、アリティに対する抽象化、特に異種リスト ( HList) としての表現を提供します。任意のアリティの関数は ( as 引数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)

ここで、どうにかしてアリティを抽出する必要があります。タイプ レベルでは、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操作がオーバーロードされていることに注意してください。強力な型制約がなければ、Scala はDouble自動的にバージョンを提供しませんがInt、何らかの理由でバージョンを使用します。

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

したがって、これを機能させるには間接化が必要math.max _: ((Double, Double) => Double)です。


これがあなたの具体的なケースで最善の方法であるとは言いませんが、楽しい探索だったと思います.

于 2013-06-19T09:59:33.923 に答える
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 _));

(型消去のために実行時に関数の引数/戻り値の型をチェックできないため、要件を満たす完全に汎用的な関数を作成することはできないと思います。)

于 2013-06-19T09:17:38.167 に答える