2016-04-28 47 views
0

的XQueryXQuery中從序列中除去連續的號碼

輸入:(1,2,3,4,5,6,7,14,15,16,17,24,25,26 ,27,28)

輸出:(1,7,14,17,24,28)

我試圖使用XQuery函數從輸入序列中刪除連續的號碼,但未能這樣做

xquery version "1.0" encoding "utf-8"; 

    declare namespace ns1="http://www.somenamespace.org/types"; 

    declare variable $request as xs:integer* external; 

    declare function local:func($reqSequence as xs:integer*) as xs:integer* { 

    let $nonRepeatSeq := for $count in (1 to count($reqSequence)) return 
          if ($reqSequence[$count+1] - $reqSequence) then 
          remove($reqSequence,$count+1) 
          else() 
    return 
    $nonRepeatSeq 
    }; 

    local:func((1,2,3,4,5,6,7,14,15,16,17,24,25,26,27,28)) 

請建議如何在XQuery功能語言中這樣做。

+2

始終提供_specific問題statement_,描述你在哪裏卡住。 「失敗」不是一個合理的問題描述,你是否收到錯誤信息,你會得到錯誤的輸出,......? –

+0

簡單卡在功能無狀態的部分.. :) –

回答

3

兩種簡單的方法在XQuery來做到這一點。兩者都依賴於能夠將值序列分配給一個變量,以便我們可以在需要時查看它的各個成員對。首先,只需迭代這些值並選擇(a)第一個值,(b)任何不比其前一個值大的值,以及(c)任何不小於其後繼者的值。 [OP指出最後一個值也需要包括在內;留給讀者作爲練習。或者看邁克爾凱的答案,它提供了一個更精確的過濾器的公式;德摩根定律罷工再次]

let $vseq := (1,2,3,4,5,6,7,14,15,16,17,24,25,26,27,28) 
for $v at $pos in $vseq 
return if ($pos eq 1 
      or $vseq[$pos - 1] ne $v - 1 
      or $vseq[$pos + 1] ne $v + 1) 
     then $v 
     else() 

或者,第二,做大致相同的事情在過濾器表達式:

let $vseq := (1,2,3,4,5,6,7,14,15,16,17,24,25,26,27,28) 
return $vseq[ 
    for $i in position() return 
     $i eq 1 
     or . ne $vseq[$i - 1] + 1 
     or . ne $vseq[$i + 1] - 1] 

執行的計算這兩種方式,你的非工作的主要區別企圖是他們沒有說改變或修改序列的任何事情;他們只是指定一個新的序列。通過使用過濾器表達式,第二個表達式明確表示結果將是$ vseq的子序列;一般來說for表達式並沒有這樣的保證(儘管因爲每個值都返回空序列或者值本身,我們可以看到這裏的結果也是一個子序列:一個$ vseq的副本,其中一些值已經被省略。

許多程序員發現很難停止變量或數據結構的修改分配的角度來思考,但它的價值的一些努力。

[附錄]我可能會忽視的東西,但我不請參閱在純XPath 2.0中表示此計算的方式,因爲XPath 2.0似乎沒有任何機制可將像$vseq這樣的變量綁定到非單例值序列(XPath 3.0有let表達式,所以這不是一個挑戰。上面的第二種配方本身是純粹的XPath 3.0)

+0

是的,它的工作原理,但它也刪除了最後一個數字,我們不想要它,因爲它結束了序列,沒有數字,因此不是連續的數字 –

+0

然而,只是最後一個數字添加到序列 –

+0

邁克爾,你可以在XPath 2.0中實現,只要輸入序列作爲參數在外部提供,或者實際上是從XML文檔的內容派生的:請參閱我的解決方案。 –

0

解決方案中有幾個邏輯和XQuery使用錯誤,但其主要問題是XQuery中的變量是不可變的,所以一旦賦值就不能重新賦值。因此,考慮這些類型的問題在遞歸解決方案方面往往更容易:

declare function local:non-consec(
    $prev as xs:integer?, 
    $rest as xs:integer* 
) as xs:integer* 
{ 
    if (empty($rest)) then() 
    else 
    let $curr := head($rest) 
    let $next := subsequence($rest, 2, 1) 
    return (
     if ($prev eq $curr - 1 and $curr eq $next - 1) 
     then() (: This number is part of a consecutive sequence :) 
     else $curr, 
     local:non-consec(head($rest), tail($rest)) 
    ) 
}; 

local:non-consec((), (1,2,3,4,5,6,7,14,15,16,17,24,25,26,27,28)) 
=> 
1 
7 
14 
17 
24 
28 
+0

使用XQuery版本1,其中頭部和尾部功能沒有在內部實現 –

+0

@KaushikBose對於XQuery 1的兼容性,您可以簡單地用'$ rest [1]替換head($ rest) '和'tail($ rest)'帶'subsequence($ rest,2)'。 – wst

2

在XSLT這是可以做到的。

<xsl:for-each-group select="$in" group-adjacent=". - position()"> 
    <xsl:sequence select="current-group()[1], current-group()[last()]"/> 
</xsl:for-each-group> 

在XQuery中3.0你可以用翻滾窗口做到這一點,但我懶得鍛鍊細節。

XPath 2。0溶液(假設輸入序列是在$in)爲:

for $i in 1 to count($in) 
return $in[$i][not(. eq $in[$i - 1]+1 and . eq $in[$i+1]-1)]