2014-03-04 50 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