scala - 找出两个变量是否从Scala中的同一参数化类型继承

标签 scala

这是我的问题:

我尝试汇总对象列表:

val list = List(Foo(1), Foo(2), Bar(2), Bar(3), Baz(5), Baz(3))

聚合之后,我希望该列表中的每种可聚合类型都只有一个对象。在此示例中,Foo和Bar应该在没有Baz的情况下是可聚合的,因此结果应为:
List(Foo(3), Bar(5), Baz(5), Baz(3))

我的想法是定义一个可聚合的特征,如下所示:
trait Aggregatable[T] {
    def aggregate(agg: T): T
}

case class Foo(val x: Int) extends Aggregatable[Foo] {
    def aggregate(agg: Foo) = {
        val x = (0 /: List(this, agg))((old, elem) => (old + elem.x))
        new Foo(x)
    }
}

case class Bar(val x: Int) extends Aggregatable[Bar] {
    def aggregate(agg: Bar) = {
        val x = (0 /: List(this, agg))((old, elem) => (old + elem.x))
        new Bar(x)
    }   
}

case class Baz(val x: Int)

好吧,我认为这是问题的明显部分。

在下一步中,我尝试汇总列表。首先,我将列表分为同构类型列表:
val grouped = list.groupBy( _.getClass().toString() )

/* => grouped should be
 * Map(
 *     class Foo -> 
 *         List(Foo(1), Foo(2)),
 *     class Bar -> 
 *         List(Bar(3), Bar(4)), 
 *     class Baz -> 
 *         List(Baz(5), Baz(3))
 * )
 */

现在为简单起见,现在让我们假设我们要确定第一个列表的前两个元素是否可聚合:
val firstList = grouped.toList.apply(0)._2 // List(Foo(1), Foo(2))
val a = firstList (0) // Foo(1)
val b = firstList (1) // Foo(2)

这是我实际问题的开始。为了确定a和b是否可以聚合,必须有一种方法来询问a和b是否从某个固定T的同一类型Aggregatable [T]继承。

我要问的方法是定义aggregatablePair类型:
type aggregatablePair = Pair[T, T] forSome { type T <: Aggregatable[T] }

用a和b建立一对:
val pair = (a, b)

并汇总它们(如果它们是可聚合对):
pair match {
    case aggPair: aggregatablePair => aggPair._1.aggregate(aggPair._2)
    case _ => println("pair is not aggregatable")
}

但这不起作用...错误是:
type mismatch; 
found: aggPair._2.type (with underlying type T forSome { type T <: Aggregatable[T] })
required: T where type T <: Aggregatable[T]

我认为这听起来像找到的类型与所需的类型相匹配...谁能告诉我为什么不这样做?
表达我想要的东西的正确方法是什么?

感谢您的任何帮助

最佳答案

对于我的问题,我找到了一个非常“令人满意”的解决方案。一般的想法是向可聚合特征添加结果类型为List [Any]的方法aggregateOrCons,该方法将聚合两个对象(如果它们来自相同类型)或返回包含输入参数的列表。

trait Aggregatable[T] {
    def aggregate(agg: T): T

    def aggregateOrCons(agg: Any): List[Any] = {
        agg match {
            case t: T => List(this.aggregate(t))
            case a => List(a, this)
        }
    }
}

现在,对我的输入参数进行排序,而不是按其类进行分组,因为我只需要确保同一类型的对象连续出现。
val list = List(new Foo(1), new Baz(1), new Baz(2), new Bar(3), new Foo(2), new Bar(4))
val sorted = list.sortWith(
    (a1, a2) => (a1.getClass().toString() compareTo a2.getClass().toString()) < 0
)

在下一步中,我定义一种方法来聚合两个类型为Any的对象。如果两个输入参数的类型均为Aggregatable,则对它们应用aggregatableOrCons方法(如果两个参数相等,则将导致两个参数的聚集,否则将导致包含参数的列表)。如果其中之一不是可聚合的,则将返回包含输入参数的列表。
def aggregate(a: Any, b: Any): List[Any] = a match {
    case agg1: Aggregatable[_] => b match {
        case agg2: Aggregatable[_] => agg1.aggregateOrCons(agg2)
        case b => List(b, agg1)
    }
    case a => List(b, a)
}

现在,唯一能够折叠已排序的输入附件列表的要求是一个中立元素。它应该与任何东西聚合,并且应该只返回输入参数。
object NeutralAggregatable extends Aggregatable[Any] {
    def aggregate(agg: Any) = agg
}

现在我可以折叠排序列表
val neutral: Any = NeutralAggregatable
val aggregated = (List(neutral) /: sorted)((old, elem) => 
    (aggregate(old.head, elem) ::: old.tail)
)

println(aggregated) // List(Foo(3), Baz(2), Baz(1), Bar(7))

关于scala - 找出两个变量是否从Scala中的同一参数化类型继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8367627/

相关文章:

java - 将二进制字符串转换为整数时 Scala 中字符串之间的区别

scala - java.io.Serialized 和 GenTraversableOnce 类型不匹配

scala - 过滤和映射包含选项的 scala 元组序列的简洁方法

scala - 如何消除 IntelliJ 14.0.3 for Play 2.3.7 应用程序中的错误下划线?

scala - Scala 可以在一个表达式中应用多个隐式转换吗?

scala - AnyRef 的奇怪模式匹配行为

java - 从 java 访问 scala 对象字段

java - 我可以在 Java 代码中使用 Scala 标准库吗?

scala - 如何在 Scala 中克隆对象?

scala - 在 Scala 中,赋值运算符 "="是方法调用吗?