在Scala中合并具有不同类型参数的两个集合

时间:2014-11-28 13:43:09

标签: scala polymorphism

我需要实现一个基本上包含键和值对的类,其中值是一系列元组。此元组包含一个对象SynthesizedMetricTag,以及一个带有Numeric上下文绑定的类型参数A,因此实际上是一个k / v序列,其中v需要是一个数字。

case class Cohort[A : Numeric](index:Any,values:Seq[(SynthesizedMetricTag,A)])

当我必须实现合并此类的两个实例的函数时,问题就出现了。更具体地说,当我必须将类型A的seq与类型B的seq合并时,问题出现了。两个类型都有Numeric上下文绑定,所以我的想法是最终得到{{1} }},它符合上下文绑定并合并Cohort[C]A类型序列的所有K / V对,而不重复任何键。

B

当然,此代码会引发以下错误:

case class Cohort[A : Numeric](index:Any,values:Seq[(SynthesizedMetricTag,A)]) {
  def merge[B : Numeric, C:Numeric](that:Cohort[B]):Cohort[C] =
    if(this.index != that.index) throw new Exception("Unable to merge Cohorts. Criteria is not the same")
    else {
      val b = new ArrayBuffer[(SynthesizedMetricTag,C)]()
      val seen = new mutable.HashSet[SynthesizedMetricTag]()
      for (x <- this.values; y <- that.values){
        if(!seen(x._1)){
          b+= x
          seen += x._1
        }
        if(!seen(y._1)){
          b+= y
          seen += y._1
        }
      }
      Cohort(this.index,b.toSeq)
    }
}

所以我尝试了以下内容:

[error] /Users/ernestrc/dev/everreach/operations-api/src/main/scala/everreach/operations/model/Cohort.scala:14: type mismatch;
[error]  found   : (everreach.operations.model.SynthesizedMetricTag, A)
[error]  required: (everreach.operations.model.SynthesizedMetricTag, C)
[error]           b+= x
[error]               ^
[error] /Users/ernestrc/dev/everreach/operations-api/src/main/scala/everreach/operations/model/Cohort.scala:18: type mismatch;
[error]  found   : (everreach.operations.model.SynthesizedMetricTag, B)
[error]  required: (everreach.operations.model.SynthesizedMetricTag, C)
[error]           b+= y

它编译,但我确信这不是这种做法的惯用方式。你们知道如何解决这个问题吗?

修改

Alexey提出了一个很好的观点:

  

如果,例如,您希望发生什么? A是Double,B是Long,和   C是Byte?

case class Cohort[A : Numeric](index:Any,values:Seq[(SynthesizedMetricTag,A)]) { def merge[B : Numeric, C:Numeric](that:Cohort[B]):Cohort[C] = if(this.index != that.index) throw new Exception("Unable to merge Cohorts. Criteria is not the same") else { val b = new ArrayBuffer[(SynthesizedMetricTag,C)]() val seen = new mutable.HashSet[SynthesizedMetricTag]() for (x <- this.values; y <- that.values){ if(!seen(x._1)){ b+= x.asInstanceOf[(SynthesizedMetricTag,C)] seen += x._1 } if(!seen(y._1)){ b+= y.asInstanceOf[(SynthesizedMetricTag,C)] seen += y._1 } } Cohort(this.index,b.toSeq) } } 需要作为类型参数传递,否则编译器不知道C是什么,所以我希望CA成为转换为B。但我真正想要的是永远不会丢失信息。因此,例如,如果CAIntB,我希望Float转换为A并合并两者FloatA到集合BC)。

当然这个层次结构不存在,是吗?这就是我需要将结果类型抽象为Float并手动将其作为类型参数传递的原因。

1 个答案:

答案 0 :(得分:2)

我只是用类型A和B的隐式证据来定义方法,直到C

case class Cohort[A <: Numeric](index:Any,values:Seq[(SynthesizedMetricTag,A)]) {
 def merge[B <: Numeric, C](that:Cohort[B])(implicit ev1:A=>C, ev2:B=>C):Cohort[C] =
   if(this.index != that.index) throw new Exception("Unable to merge Cohorts. Criteria is not the same")
   else {
     val b = new ArrayBuffer[(SynthesizedMetricTag,C)]()
     val seen = new mutable.HashSet[SynthesizedMetricTag]()
     for (x <- this.values; y <- that.values){
       if(!seen(x._1)){
         b += x._1 -> ev1(x._2)
         seen += x._1
       }
       if(!seen(y._1)){
         b+= y._1 -> ev2(y._2)
         seen += y._1
       }
     }
     Cohort(this.index,b.toSeq)
   }
}