2015-10-17 51 views
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) 

,但有没有办法通过递归做到这一点,不是decending和解析,但上升和建设?

回答

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

无论reverse(...)实际上会要求反转输出还是取决于你的XQuery引擎。最后,倒退不是增加了计算复杂度,并且可能不仅会导致更干净的代码,而且更快的执行!

类似的可以通过颠倒一切来实现,但是在此之前没有获取最后一个元素和所有内容的函数,并且在使用谓词last()position() < last()时可能会降低性能。您可以使用XQuery数组,但必须在每次递归函数调用中传递计数器。

最终最快的解决方案需要使用特定的XQuery引擎和代码进行基准测试。

+0

谢谢,@Jens Erat。因此,通过使用'tail()'和'head()',可以在没有'for'表达式的情况下迭代,因为tail()在(移动)head()之前全部返回。凉。我会看看我是否可以充分利用它自己... ...。 –

+1

'tail($ item)'和'head($ item')只是'$ item [1]'各自的'$ item [position()> 1]'的语法糖,但它们导致代码更容易阅读,尤其是对具有强大功能背景的开发人员。 –

相关问题