2

给定以下 XML 元素——

val nodes = List(
    <foo/>,
    <bar/>,
    <baz/>,
    <bar>qux</bar>,
    <bar quux="corge"/>,
    <bar quux="grauply">waldo</bar>,
    <bar quux="fred"></bar>
)

-- 如何构造一个匹配所有<bar/>s 的模式?我试过了,例如:

nodes flatMap (_ match {
  case b @ <bar/> => Some(b)
  case _ => None
})

但这仅匹配空。

res17: List[scala.xml.Elem] = List(<bar/>, <bar quux="corge"/>, <bar quux="fred"></bar>)

如果我允许内容占位符:

nodes flatMap (_ match {
  case b @ <bar>{content}</bar> => Some(b)
  case _ => None
})

这仅匹配空。

res20: List[scala.xml.Elem] = List(<bar>qux</bar>, <bar quux="grauply">waldo</bar>)

我当然可以放弃 XML 文字而只写

nodes flatMap (_ match {
  case e: Elem if e.label == "bar" => Some(e)
  case _ => None
})

但似乎必须有更聪明的方法。

4

1 回答 1

4

您可以使用Elem对象来匹配:

nodes collect { case b @ Elem(_, "bar", _, _, _*) => b }

的来源在Elem这里所以你可以看到 的定义unapplySeq。消息来源甚至有评论:

可以使用语法解构任何Node实例(不是 aSpecialNode或 a )Groupcase Elem(prefix, label, attribs, scope, child @ _*) => ...

另一种选择是使用模式替代品

 nodes collect { case b @ (<bar/> | <bar>{_}</bar>) => b }

请注意,模式替代项不能绑定通配符以外的变量。

如果这对您来说是一种常见操作,那么您可能会考虑编写自己的提取器(如此所述)。例如:

object ElemLabel { 
    def unapply(elem: Elem): Option[String] = Some(elem.label) 
}

接着:

nodes collect { case b @ ElemLabel("bar") => b }

当然,在您提供的示例中,您只是过滤,在这种情况下:

nodes filter { _.label == "bar" }

就足够了,可能是你最好的选择。即使您计划在过滤器之后执行一些其他操作,并且您关心性能和构建中间集合,您也可以使用 aview并消除这种担忧。

还要注意throughput的使用collect,这是一种更惯用的方式来进行过滤、映射和匹配,你正在使用flatMapmatchOption

于 2015-02-18T01:05:22.437 回答