从 XQuery 中的序列中删除连续的数字

Removing consecutive numbers from a sequence in XQuery

XQuery

输入: (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 函数式语言执行此操作。

您的解决方案中存在多个逻辑和 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

在 XQuery 中执行此操作的两种简单方法。两者都依赖于能够将值序列分配给一个变量,这样我们就可以在需要时查看它的成对的单个成员。

首先,只需遍历值和 select (a) 第一个值,(b) 不大于前一个值的任何值,以及 (c) 不小于前一个值的任何值比它的继任者。 [OP 指出还需要包括最后一个值;留作 reader 的练习。或者查看 Michael Kay 的回答,它提供了过滤器的更简洁的表述;德摩根定律再次出击!]

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 表达式通常没有这样的保证(尽管因为对于每个值它 returns 要么是空序列要么是值本身,我们可以看到这里的结果也将是一个子序列: $vseq 从中省略了一些值。

许多程序员发现很难停止对变量赋值或数据结构修改的思考,但这值得付出一些努力。

[附录] 我可能忽略了一些东西,但我没有看到在纯 XPath 2.0 中表达此计算的方法,因为 XPath 2.0 似乎没有任何可以绑定变量的机制,例如 $vseq 到非单例值序列。 (XPath 3.0 有 let 表达式,所以这不是一个挑战。上面的第二个公式本身就是纯 XPath 3.0。)

在 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 中,您可以通过翻滚来完成 windows,但我懒得弄清楚细节了。

一个 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)]