HXT: как «поднять» детей на определенные элементы? - PullRequest
0 голосов
/ 27 августа 2018

Предположим, у меня есть этот документ 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>

Предположим, я хочу «поднять» дочерние элементы mi и mrow, я должен получить (давайте проигнорируем изменение пробела здесь)

<?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 голосов
/ 28 августа 2018

Краткое описание типа

XmlTree = NTree XNode

означает, что каждый XmlTree имеет конструкцию

NTree XNode [XMLTree]

Где первый аргумент - текущий узел, а второй аргумент - список дочерних элементов.

Творческий процесс

processTopDown примет преобразование дерева, которое вы предоставляете, и произведет преобразование дерева, которое применяет его рекурсивно.

Сначала давайте определим преобразование дерева, которое вы хотите на одном узле:

  1. Пройти через дочерние узлы текущего узла
  2. Если они совпадают с указанными нами тегами, то
    1. Возьмите всех детей тега и
    2. Вместо этого сделайте их потомками текущего узла
    3. Затем удалите тег

Преобразование не «поднимает» дочерние элементы текущего узла, потому что это не было бы возможно для корня.

Хороший способ сделать это - использовать processChildren - стрелку, которая позволяет нам указывать новых дочерних элементов для текущего узла на основе старых дочерних элементов. Для этого нам понадобится условные стрелки

Затем мы можем разделить дизайн на две части: предикат для сопоставления желаемых тегов и преобразование, которое мы хотим выполнить

Предикат

Напоминаем себе о , какие формы может принимать XNode, тот, который нас интересует,

XTag QName XmlTrees

Мы хотим сопоставить узлы этой формы с заданными нами именами тегов. Для этого напишем вспомогательную функцию

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

Для простоты использования мы хотим записать теги в виде строк, поэтому мы будем использовать mkName для преобразования их в QName s. Тогда наша более полезная функция фильтра -

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