HXT:如何“提升”某些元素的孩子?

时间:2018-08-27 19:14:08

标签: xml haskell hxt

假设我有此MathML文档

<?xml version="1.0" encoding="UTF-8”?>
<math xmlns="http://www.w3.org/1998/Math/MathML">
    <mi> f </mi> 
    <mo> &ApplyFunction; </mo> 
    <mrow> 
      <mo> ( </mo> 
      <mi> x </mi> 
      <mo> ) </mo> 
    </mrow> 
</math>

假设我想“举起” mimrow的孩子,我应该得到(让我们忽略此处的空白更改)

<?xml version="1.0" encoding="UTF-8"?>
<math xmlns="http://www.w3.org/1998/Math/MathML">
    f
    <mo> &ApplyFunction; </mo> 
    <mo> ( </mo> 
    x
    <mo> ) </mo> 
</math>

我应该如何用HXT编写它?

我是Haskell新手...所以我现在所拥有的只是

-- Dealing with command line arguments and stuff…
processRootElement :: IOSArrow XmlTree XmlTree
processRootElement
    = processTopDown -- What goes here?

1 个答案:

答案 0 :(得分:3)

快速键入摘要

XmlTree = NTree XNode

表示每个XmlTree都有构造

NTree XNode [XMLTree]

第一个参数是当前节点,第二个参数是子节点列表。

创作过程

processTopDown将接受您提供的树形转换,并生成一个递归应用的树形转换。

首先,让我们在单个节点上定义所需的树转换:

  1. 浏览当前节点的子节点
  2. 如果与我们指定的标签匹配,则
    1. 获取标签的所有子元素,并且
    2. 改为让他们成为当前节点的子代
    3. 然后删除标签

该转换不会“提升”当前节点的子节点,因为这在根节点上是不可能的。

执行此操作的一个好方法是使用processChildren,这是一个箭头,可让我们基于旧子节点为当前节点指定新子节点。为此,我们需要使用conditional arrows

然后我们可以将设计分为两部分,一个用于匹配我们想要的标签的谓词,另一个是我们要执行的转换

谓词

提醒自己what formsXNode可以采取的行动,我们感兴趣的是

XTag QName XmlTrees

对于给定的标签名称,我们希望在这种形式的节点上进行匹配。为此,我们编写了一个辅助函数

filterOnQName :: QName -> XNode -> Bool
filterOnQName qname (XTag xqname _) 
  | qname == xqname = True
  | otherwise       = False
filterOnQName _ _   = False

为了易于使用,我们希望将标签写为字符串,因此我们将使用mkName将它们转换为QName。那么我们更有用的过滤器功能是

filterTags :: [String] -> XmlTree -> Bool
filterTags tagNames (NTree xnode _) = any (\qname -> filterOnQName qname xnode) (map mkName tagNames)

但这不是箭头,出于以后我们将要看到的原因,我们需要它。我们可以简单地使用isA

将其变成一个
childFilter tags = isA (filterTags tags)

转型

我们要使用两个箭头来表示变换主体-一个用于过滤器匹配时,另一个用于当过滤器不匹配时。

对于不这样的情况,转换很容易-我们想保留当前节点。

filterFailed = this

在这里,this是身份箭头-它什么也不做。

当过滤器匹配时,我们想得到孩子-首先,让我们编写一个帮助程序

getChildren :: XmlTree -> [XmlTree]
getChildren (NTree _ children) = children

由于我们正在使用列表箭头,因此可以方便地使用arrL

将其直接变成箭头
liftChildren = arrL getChildren

将它们组合

我们现在可以使用ifA的箭头版本if将其变成单个箭头

liftMatchedChildren tags = ifA (childFilter tags) liftChildren filterFailed

最后,我们可以描述所需的转换

processRootElement
  = processTopDown (processChildren (liftMatchedChildren ["mi", "mrow"]))