两个对象扩展一个基本对象,如下所示:
trait Component
class ComponentA extends Component
class ComponentB extends Component
假设我实例化了一些对象:
val a = new ComponentA
val a2 = new ComponentA
val b = new ComponentB
我得到了一个类型为Component
的列表,如下所示:
val components = List[Components](a, a2, b)
我想做的是有一个函数,它接受一个混合类型的列表,并将其转换为只包含一个类型的n元组列表。例如,components
会像这样分解:
transform.apply(components) = (List(a, a2), List(b))
做到这一点的非功能、怪诞、弗兰肯斯坦式的方法是:
def apply(c: List[Component]) = {
var aComponents = new ListBuffer[ComponentA]()
var bComponents = new ListBuffer[ComponentB]()
for (c <- components) {
c match {
case aType: ComponentA => aComponents += aType
case bType: ComponentB => bComponents += bType
case _ => None
}
}
aComponents -> bComponents
}
显然,这种方法有很多缺点:
- 在我的例子中,我有两种组件类型。但这种情况并不能推广到产生n分量的n元组
- 它不起作用。(使用可变的
ListBuffer
,不利用Collections
库。( - 太难看了。就像真的很丑陋。我再怎么强调也不为过
有没有一种方法可以在Scala中使用FP来实现这一点?我最初的想法是使用某种带有内部事例/匹配的groupBy
函数进行类型检查?
好吧,为什么不把groupBy
和你的类一起使用呢
c.groupBy(_.getClass).values
这已经为您提供了Lists
的List
,其中包含分组实例。获取元组更为复杂,因为您需要事先了解类型。有一些解决方案使用Shapeless(将Scala列表转换为元组?(,但不确定你真的必须走那么远,因为你的原始解决方案似乎在没有实际元组的情况下也能工作。
我会使用collect:
val aList = c.collect { case a: ComponentA => a }
val bList = c.collect { case b: ComponentB => b }
val res = (aList, bList)
考虑使用foldRight
遍历components
列表以组装(List[ComponentA], List[ComponentB])
元组:
components.foldRight( (List[ComponentA](), List[ComponentB]()) )(
(x, acc) => x match {
case x: ComponentA => (x :: acc._1, acc._2)
case x: ComponentB => (acc._1, x :: acc._2)
case _ => acc
}
)
// res1: (List[ComponentA], List[ComponentB]) =
// (List(ComponentA@24a298a6, ComponentA@74fe5966), List(ComponentB@2bfbffb2))