将XML转换为Scala中的元素列表

时间:2014-08-27 06:30:48

标签: xml scala collections

这是我上一个问题的后续行动。我想热切地(而不是懒惰)线性化XML树。为简单起见,我假设XML树是Elem节点的树,因此我将XML转换为Elem的序列。

import scala.xml.{Elem, Node}
import PartialFunction._

def linearize(node: Node): List[Node] = {
  val children = node.child.filter(cond(_) {case _: Elem => true}).toList
  children match {
    case Nil => List(node)
    case list => node :: list.flatMap(linearize)
  }
}

看起来很有效,但我不喜欢val children = ...您如何建议更改/修复它?

2 个答案:

答案 0 :(得分:2)

由于您已经将node.child转换为List,因此您无需在其上进行匹配,您可以直接使用flatMap,因为如果child为空,它将返回Nil 。以下代码产生与简单测试用例的解决方案相同的结果。

import scala.xml.{Elem, Node}
import PartialFunction._

def linearize(node: Node): List[Node] = {

  node :: node.child.flatMap {
    case e: Elem => linearize(e)
    case _ => Nil
  }.toList

}

答案 1 :(得分:1)

您可以depth-first and breath-first方式遍历树。我已经为这两种模式创建了功能。两者都是尾递归的,所以你可以牺牲堆来节省一些堆栈帧。

import scala.xml.{ Elem, Node }

def linearizeDepthFirst(node: Node): List[Node] = {
  @annotation.tailrec
  def loop(toVisit: Vector[Node], result: Vector[Node]): List[Node] = {
    if (toVisit.isEmpty) {
      result.toList
    } else {
      val children = toVisit.head.collect({ case e: Elem => e }).toVector
      loop(children ++ toVisit.tail, result ++ children)
    }
  }

  loop(Vector(node), Vector(node))
}

def linearizeBreadthFirst(node: Node): List[Node] = {
  @annotation.tailrec
  def loop(toVisit: Vector[Node], result: Vector[Node]): List[Node] = {
    if (toVisit.isEmpty) {
      result.toList
    } else {
      val children = toVisit.head.collect({ case e: Elem => e }).toVector
      loop(toVisit.tail ++ children, result ++ children)
    }
  }

  loop(Vector(node), Vector(node))
}