Как мне сложить узлы элемента XML с помощью линзы? - PullRequest
1 голос
/ 26 июня 2019

Я использую пакет xml-lens для обработки XML.

Учитывая Element, я хочу выполнить concatMap -подобные вычисленияего elementNode :: [Node] поле.В частности, NodeElement, удовлетворяющий некоторому условию, должен произвести еще несколько NodeElement s, а все остальные случаи (остальные NodeElement s и другие конструкторы Node должны создать одноэлементный список. Затем он должен быть объединен в [Node] и используется в качестве значения для перезаписи текущего elementNode заданного Element.

Я пытаюсь написать правильное заклинание линзы, чтобы сделать это. Вот что мне удалось придумать до сих пор:

over nodes $ concatMapOf someLensMagic (myFun :: Element -> [Node])

Это проверка типов, но я не могу реализовать someLensMagic. Помощь с этим будет очень признателен.

Ответы [ 4 ]

1 голос
/ 30 июня 2019

Самое простое решение, очень похоже на то, что предлагает Бергей , написав обертку вокруг myFun, чтобы она могла обрабатывать Node с, а не Element с:

myFun' :: Node -> [Node]
myFun' n = case n of
    NodeElement el -> myFun el
    _ -> [n]

myFun' может затем использоваться с простым concatMap (в отличие от concatMapOf) для изменения поля nodes:

over nodes (concatMap myFun')

Как оказалось, есть кусок объектива магии, который можно использовать как альтернативный способ написания обертки. Комбинатор outside превращает призму в линзу, нацеленную на функцию, что позволяет нам, по сути, редактировать, что функция делает в конкретном случае. На практике это выглядит так:

over nodes (concatMap $ set (outside _Element) myFun (:[]))

Аргумент concatMap - это функция, которая ведет себя подобно (:[]), за исключением случаев, когда аргумент соответствует _Element, и в этом случае он становится myFun в базовом элементе. То есть, по сути, абстрактное сопоставление с образцом. Чтобы понять суть дела, мы можем попробовать имитировать стиль определения myFun' выше, используя outside:

myFun' :: Node -> [Node]
myFun'
    = outside _Element .~ myFun
    $ \n -> [n]
1 голос
/ 26 июня 2019

Если myFun объясняет, что вы хотите сделать с конструктором NodeElement, и вам нужен пустой список для других конструкторов Node, вы можете использовать concatMapOf _Element.

Чтобы обеспечить одноэлементный список для других конструкторов, вы должны изменить myFun для обработки всех конструкторов Node. Тогда вы можете использовать concatMap myFun, а не concatMapOf. (Вы можете использовать concatMapOf traverse myFun в этом случае, но это сложнее понять.)

Нет способа написать someLensMagic, который сгенерирует одноэлементный список, потому что нет способа извлечь Element из NodeComment (например).

0 голосов
/ 27 июня 2019

Я получил два последовательных concatMap с:

over nodes (concatMap $ concatMapOf _Element myFun) element

Это неоптимальное решение, поэтому я не отмечаю его как ответ и приветствую любые другие предложения.

0 голосов
/ 26 июня 2019

Один из способов обработки определенных дочерних узлов по-разному в Fold это использование to для помещения их в разные ветви Either в соответствии с условием, а затем использование beside, функция, которая позволяет применять различные Traversal к каждой ветви Either.

Например, эта функция должна возвращать дочерние элементы Node s Element с учетом того, что для узлов, которые удовлетворяют условию, вместо них возвращаются их дочерние узлы:

foldDifferently :: (Node -> Bool) -> Fold Element Node
foldDifferently p =
      nodes 
    . folded 
    . to (\n -> if p n then Right n else Left n) 
    . beside id (_Element . nodes . folded)
...