2015-10-17 74 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]'的語法糖,但它們導致代碼更容易閱讀,尤其是對具有強大功能背景的開發人員。 –

相關問題