2014-03-04 93 views
4

我想定義一個函數,該函數通過一般的產品類型進行參數化,但可以計算產品的實體。這是一個示例代碼片段。我想在調用f(...)時執行arity檢查,而不是調用f(...)()時。我怎樣才能做到這一點?查找產品類型的實體,不包含實例

def f[T<:Product](names:Seq[String], values:()=>T) = { 
() => { 
    val x = values() 
    if (x.productArity != names.size) scala.sys.error("Size mismatch") 
    names.zip(x.productIterator.map(_.toString).toSeq).map(kv => kv._1+"="+kv._2) 
    } 
} 

(這是一個很沒用的功能,只是爲了演示。最重要的點是:(1)它是由產品類型參數,(2)如果該產品的元數相匹配的功能纔有意義(3)當我調用函數時,獲取產品的實例是昂貴的/不可能的我的實際用例是一個用於寫出SQL語句的實用類,它基於火花RDD。)

如果有必要,我可以寫出一整套函數,每一個函數都適用於Tuple的每個尺寸。但那感覺很糟糕,我希望有更好的解決方案。

回答

2

比寫不同的方法更好的位可以使用類型類中找到:

case class Arity[P](get: Int) 

object Arity { 
    def apply[P](implicit arity: Arity[P]) = arity 
    implicit def tuple2[A,B] = Arity[(A,B)](2) 
    implicit def tuple3[A,B,C] = Arity[(A,B,C)](3) 
    //... 
} 

def f[T<:Product:Arity](names:Seq[String], values:()=>T) = { 
() => { 
    val x = values() 
    if (Arity[T].get != names.size) scala.sys.error("Size mismatch") 
    names.zip(x.productIterator.map(_.toString).toSeq).map(kv => kv._1+"="+kv._2) 
    } 
} 

當然,你需要寫下來Arity對象爲所有可能的元組大小。您可以使用代碼生成或使用宏自動化(如果您敢於冒險)。

0

你可以嘗試定義names爲同一亞型的Product

def f[T<:Product](names: T, values:()=>T) = { 
() => { 
    val x = values() 
    names.productIterator.toList.zip(x.productIterator.map(_.toString).toSeq).map(kv => kv._1+"="+kv._2) 
    } 
} 

scala> f(("one", "two"),() => ("1", "2"))() 
res15: List[java.lang.String] = List(one=1, two=2) 

scala> f(("one", "two"),() => ("1"))() 
<console>:9: error: inferred type arguments [java.io.Serializable] do not conform to method f's type parameter bounds [T <: Product] 
       f(("one", "two"),() => ("1"))() 
      ^

scala> f(("one"),() => ("1", "2"))() 
<console>:9: error: inferred type arguments [java.io.Serializable] do not conform to method f's type parameter bounds [T <: Product] 
       f(("one"),() => ("1", "2"))() 
      ^

當然它使得不方便名字傳遞,如果你在某種收藏有他們。您可以使用其中一種方法將List轉換爲Tuple,例如:Is there way to create tuple from list(without codegeneration)?

也許使用HList代替Product是不是一個壞主意;)

0

下面是使用反射解決方案,這將對於產品1-22工作:

import scala.reflect.runtime.universe._ 

def tupleArity(typ: Type): Int = { 
    for(i <- 2 to 22) { 
     if (typ.member(stringToTermName("_" + i)) == NoSymbol) return i - 1 
    } 
    22 
} 

小例子:

def f[T <: Product](implicit tag: TypeTag[T]): Int = tupleArity(typeOf[T]) 

scala> f[Tuple2[_, _]] 
res18: Int = 2