假设我想要展平相同类型的嵌套列表......例如
ListA(Element(A), Element(B), ListA(Element(C), Element(D)), ListB(Element(E),Element(F)))
ListA
包含相同类型的嵌套列表(ListA(Element(C), Element(D))
),所以我想用它包含的值替换它,所以上面示例的结果应如下所示:
ListA(Element(A), Element(B), Element(C), Element(D), ListB(Element(E),Element(F)))
当前的类层次结构:
abstract class SpecialList() extends Exp {
val elements: List[Exp]
}
case class Element(name: String) extends Exp
case class ListA(elements: List[Exp]) extends SpecialList {
override def toString(): String = "ListA("+elements.mkString(",")+")"
}
case class ListB(elements: List[Exp]) extends SpecialList {
override def toString(): String = "ListB("+elements.mkString(",")+")"
}
object ListA{def apply(elements: Exp*):ListA = ListA(elements.toList)}
object ListB{def apply(elements: Exp*):ListB = ListB(elements.toList)}
我已经制定了三个有效的解决方案,但我认为必须有更好的方法来实现这一目标:
第一个解决方案:
def flatten[T <: SpecialList](parentList: T): List[Exp] = {
val buf = new ListBuffer[Exp]
for (feature <- parentList.elements) feature match {
case listA:ListA if parentList.isInstanceOf[ListA] => buf ++= listA.elements
case listB:ListB if parentList.isInstanceOf[ListB] => buf ++= listB.elements
case _ => buf += feature
}
buf.toList
}
第二个解决方案:
def flatten[T <: SpecialList](parentList: T): List[Exp] = {
val buf = new ListBuffer[Exp]
parentList match {
case listA:ListA => for (elem <- listA.elements) elem match {
case listOfTypeA:ListA => buf ++= listOfTypeA.elements
case _ => buf += elem
}
case listB:ListB => for (elem <- listB.elements) elem match {
case listOfTypeB:ListB => buf ++= listOfTypeB.elements
case _ => buf += elem
}
}
buf.toList
}
第三种解决方案
def flatten[T <: SpecialList](parentList: T): List[Exp] = parentList.elements flatMap {
case listA:ListA if parentList.isInstanceOf[ListA] => listA.elements
case listB:ListB if parentList.isInstanceOf[ListB] => listB.elements
case other => List(other)
}
我的问题是,是否有更好,更通用的方法来实现相同的功能,因为在所有上三个解决方案中都有重复的代码?
答案 0 :(得分:13)
真正的功能性方式。不使用变量。
def flatten[A](list: List[A]): List[A] = list match {
case Nil => Nil
case (ls: List[A]) :: tail => flatten(ls) ::: flatten(tail)
case h :: tail => h :: flatten(tail)
}
答案 1 :(得分:0)
我更喜欢递归方式。一般来说,我会做这样的事情:
def flattenList[A](l: List[Any]): List[A] = {
var acc = List[A]()
l foreach ( entry => entry match {
case a: List[Any] => acc = acc ::: flattenList(a)
case b: A => acc = acc :+ b
})
acc
}
这会使您的List(Element(A), Element(B), List(Element(C), Element(D), List(Element(E), Element(F))))
变为List(Element(A), Element(B), Element(C), Element(D), Element(E), Element(F))
答案 2 :(得分:0)
在理想的世界中,如果不存在可怜的类型擦除,你会做这样的事情:
// WON'T WORK
def flatten[T <: SpecialList](parentList: T): List[Exp] = parentList.elements flatMap {
case x:T => x.elements
case somethingElse => List(somethingElse)
}
但在我看来,这种情况下的最佳解决方案是:
def flatten[T <: SpecialList](parentList: T): List[Exp] = parentList.elements flatMap {
case x:SpecialList if x.getClass == parentList.getClass => x.elements
case somethingElse => List(somethingElse)
}
它比问题中提出的更通用,因为你不需要打扰参数是ListA还是ListB,如果将来你将添加一个ListC,它也会起作用。
然而,这并不能解决你在任意深度展平的更普遍的问题,因为flatten(ListA(...))最后也必须返回一个ListA(...) - 在上面的情况下它返回列表失去了它的初始含义。解决这个问题的方法可能是:
abstract class SpecialList {
val elements: List[Exp]
def flatten: SpecialList = createAnother(elements flatMap {
case x: SpecialList => {
val flattenX = x.flatten
if (flattenX.getClass == this.getClass) flattenX.elements else List(flattenX)
}
case somethingElse => List(somethingElse)
})
// Creates another special list of the same type
def createAnother(elements: List[Exp]): SpecialList
}
case class ListA(elements: List[Exp]) extends SpecialList {
override def toString: String = "ListA("+elements.mkString(",")+")"
def createAnother(elements: List[Exp]) = ListA(elements)
}
case class ListB(elements: List[Exp]) extends SpecialList {
override def toString: String = "ListB("+elements.mkString(",")+")"
def createAnother(elements: List[Exp]) = ListB(elements)
}
这种情况下的问题是createAnother
位是纯样板文件。另一方面,这个版本保持了上述解决方案的一般性。
第三个建议,可能涉及更多地重构您的代码是完全删除ListA和ListB类型,因为在我看来,他们的目的是为Exp列表提供标记。所以考虑这个解决方案:
case class SpecialList(tag: Tag, elements: List[Exp]) extends Exp {
def flatten: SpecialList = {
val newElems = elements flatMap {
case x: SpecialList => {
val flattenX = x.flatten
if (flattenX.tag == this.tag) flattenX.elements else List(flattenX)
}
case somethingElse => List(somethingElse)
}
SpecialList(tag, newElems)
}
override def toString = tag.toString ++ "(" + elements.mkString(",") + ")"
}
sealed abstract class Tag {
// Syntactic sugar to maintain the notation used in the question
def apply(elements: Exp*): SpecialList = SpecialList(this, elements.toList)
}
object ListA extends Tag { override val toString = "ListA" }
object ListB extends Tag { override val toString = "ListB" }
从句法的角度来看,它几乎是一样的,因为你有
val x = ListA(Element(A), Element(B), ListA(Element(C), Element(D)), ListB(Element(E),Element(F), ListA(Element(C), ListA(Element(D)))))
x.flatten => ListA(Element(A),Element(B),Element(C),Element(D),ListB(Element(E),Element(F),ListA(Element(C),Element(D))))
这可能不适合你的问题,但是,如果我在那里稍微偏离轨道,那就很抱歉。