2014-11-28 3 views
0

Мне нужно реализовать класс, в котором в основном содержится ключ и пара значений, где значение представляет собой последовательность кортежей. Этот кортеж содержит объект SynthesizedMetricTag и параметр типа A с привязкой к контексту Numeric, поэтому эффективно последовательность k/v, где v должно быть числом.Слияние двух коллекций с различными параметрами типа в Scala

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

Проблема возникает, когда мне нужно реализовать функцию, которая объединяет два экземпляра этого класса. Более конкретно, проблема возникает, когда мне приходится объединять seq типа A с seq типа B. Оба типа имеют связанный с Numeric контекст, поэтому идея заключается в том, что в конце я получаю Cohort[C], который соответствует привязке к контексту и объединяет все пары K/V последовательностей типа A и 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 

Так что я попытался следующие:

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

И это компилируется, но я уверен, что это не идиоматический способ сделать это. Вы, ребята, знаете, как решить эту проблему?

EDIT

Алексей сделал хороший момент:

Что именно вы хотите, если, например, A - Double, B - Long, а C - Byte?

Ну C должен быть передан в качестве параметра типа, так как в противном случае компилятор не знает, что C, поэтому я хотел бы A и B быть преобразованы в C. Но я действительно хочу, чтобы никогда не терять информацию. Так что если, например, A является Int и B является Float, я хочу A быть преобразованы в Float и объединить оба A и B в коллекцию C (Float).

Конечно, эта иерархия не существует, не так ли? Вот почему мне нужно абстрагировать тип результата в C и вручную передать его как параметр типа.

+0

Что именно вы хотите, чтобы произошло, если, например, 'A' -' Double', 'B'' '' '' '' '' '' '' Byte'? –

+0

Ну 'C' нужно передать как параметр типа, потому что иначе компилятор не знает, что такое' C', поэтому я хотел бы, чтобы 'A' и' B' были преобразованы в 'C'. Но я действительно хочу, чтобы никогда не терять информацию. Итак, если, например, 'A' является' Int' и 'B' является' Float', я хочу, чтобы 'A' был преобразован в' Float' и объединил как 'A', так и' B' в коллекцию 'C '(' Float'). Конечно, эта иерархия не существует, не так ли? Вот почему мне нужно абстрагировать тип результата на C и определить его вручную. – ernestRC

ответ

2

Я бы просто определить метод с неявным доказательства из типов А и В типа 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) 
    } 
}