HXT:如何“提升”children某些元素?

HXT: how to “lift” children of certain elements?

假设我有这个 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的children,我应该得到(让我们忽略这里的空白变化)

<?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?

快速输入摘要

XmlTree = NTree XNode

意思是每个XmlTree都有结构

NTree XNode [XMLTree]

其中第一个参数是当前节点,第二个参数是children的列表。

创作过程

processTopDown 将采用您提供的树转换并生成递归应用它的树转换。

首先,让我们在单个节点上定义您想要的树转换:

  1. 遍历当前节点children
  2. 如果有匹配我们指定的标签,那么
    1. 取标签的所有children,
    2. 使它们成为当前节点的 children
    3. 然后删除标签

转换不会 "lift" 当前节点的 children,因为这在根节点上是不可能的。

一个好的方法是使用 processChildren,这是一个箭头,它让我们根据旧的 children 为当前节点指定新的 children。为此,我们需要使用 conditional arrows

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

谓词

提醒自己 what forms 一个 XNode 可以拿,我们感兴趣的是

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 是标识箭头 - 它什么都不做。

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

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"]))