如何使用 lens 折叠 XML 元素的节点?

How do I fold nodes of an XML element using lens?

我正在使用 xml-lens 包来处理 XML。

给定一个 Element,我想对其 elementNode :: [Node] 字段执行类似 concatMap 的计算。具体来说,满足某些条件的 NodeElement 应该产生更多的 NodeElements,而所有其他情况(NodeElements 的其余部分和其他 Node 构造函数)应该产生一个单例列表.然后它应该连接成 [Node] 并用作覆盖给定 Element.

的当前 elementNode 的值

我正在努力编写适当的镜头咒语来做到这一点。到目前为止,这是我设法想出的:

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

它进行了类型检查,但我未能实施 someLensMagic。对此的帮助将不胜感激。

如果 myFun 解释了您想用 NodeElement 构造函数做什么,并且您想要其他 Node 构造函数的空列表,则可以使用 concatMapOf _Element

要为其他构造函数提供单例列表,您应该修改 myFun 以处理 Node 的所有构造函数。那么你可以使用concatMap myFun,而不是concatMapOf。 (在这种情况下你可以使用 concatMapOf traverse myFun,但它更难理解。)

无法编写将生成单例列表的 someLensMagic,因为无法从 NodeComment 中提取 Element(例如)。

Fold 中以不同方式处理某些子节点的一种方法是使用 to to put them in different branches of an Either according to the condition, and then use beside,该函数可让您将不同的 Traversal 应用于 [=13] 的每个分支=].

比如这个函数应该return一个Element的子节点Node,特别是对于满足条件的节点,其子节点是return改为:

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)

我已经结束了两个连续的 concatMaps:

over nodes (concatMap $ concatMapOf _Element myFun) element

这是一个次优的解决方案,所以我不会将其标记为答案并欢迎任何其他建议。

最直接的解决方案是,,围绕 myFun 编写一个包装器,以便它可以处理 Nodes 而不是 Elements:

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

myFun' 然后可以与普通 concatMap(与 concatMapOf 相对)一起使用来修改 nodes 字段:

over nodes (concatMap myFun')

碰巧,有一个 lens 魔法可以用作编写包装器的替代方法。 outside 组合器将棱镜变成针对函数的透镜,实际上允许我们编辑函数在特定情况下的作用。实际上,它看起来像这样:

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

concatMap 的参数是一个函数,其行为类似于 (:[]),除非参数匹配 _Element,在这种情况下,它在基础元素上变为 myFun。也就是说,本质上是抽象的模式匹配。为了说明问题,我们可以尝试使用 outside:

来模仿上面 myFun' 的定义风格
myFun' :: Node -> [Node]
myFun'
    = outside _Element .~ myFun
    $ \n -> [n]