递归地包装一个元素

4
假设我有一个元素<x>x</x>和一些空元素(<a/>, <b/>, <c/>),我想将第一个元素包裹在第二个元素中,依次进行,结果为<c><b><a><x>x</x></a></b></c>。当我不知道空元素的数量时,我该如何做呢?
我可以这样做:
xquery version "3.0";

declare function local:wrap-up($inner-element as element(), $outer-elements as element()+) as element()+ {
    if (count($outer-elements) eq 3)
    then element{node-name($outer-elements[3])}{element{node-name($outer-elements[2])}{element{node-name($outer-elements[1])}{$inner-element}}}
    else 
        if (count($outer-elements) eq 2)
        then element{node-name($outer-elements[2])}{element{node-name($outer-elements[1])}{$inner-element}}
        else
            if (count($outer-elements) eq 1)
            then element{node-name($outer-elements[1])}{$inner-element}
            else ($outer-elements, $inner-element)
};

let $inner-element := <x>x</x>
let $outer-elements := (<a/>, <b/>, <c/>)

return 
    local:wrap-up($inner-element, $outer-elements)

但是有没有一种通过递归完成而不是下降和解析,而是上升和构建的方法呢?
1个回答

5

在函数式编程中,通常会尝试使用列表的第一个元素和尾部进行操作,因此典型的解决方案是在将元素嵌套之前反转输入:

declare function local:recursive-wrap-up($elements as element()+) as element() {
  let $head := head($elements)
  let $tail := tail($elements)
  return
    element { name($head) } { (
      $head/@*,
      $head/node(),
      if ($tail)
      then local:recursive-wrap-up($tail)
      else ()
    ) }
};

let $inner-element := <x>x</x>
let $outer-elements := (<a/>, <b/>, <c/>)

return (
    local:wrap-up($inner-element, $outer-elements),
    local:recursive-wrap-up(reverse(($inner-element, $outer-elements)))
)

无论是否需要翻转输出,取决于您的XQuery引擎。最终,翻转并不增加计算复杂度,而且可能不仅会导致更清晰的代码,甚至还能提高执行速度!
通过将所有内容颠倒过来也可以实现类似的效果,但是没有获取最后一个元素和该元素之前所有内容的函数,并且在使用谓词last()和position() < last()时可能会降低性能。您可以使用XQuery数组,但是必须在每个递归函数调用中传递计数器。
最终哪种解决方案最快,需要使用特定的XQuery引擎和代码进行基准测试。

谢谢,@Jens Erat。因此,通过使用´tail()´和´head()´,可以在不使用for表达式的情况下进行迭代,因为tail()返回移动head()之前的所有内容。很酷。我会看看自己是否足够理解并使用它.... - Jens Østergaard Petersen
1
tail($item)head($item)只是$item[1]$item[position() > 1]的语法糖。但它们产生的代码更易于阅读和理解,特别是对具有强大函数式背景的开发人员来说。 - Jens Erat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接