【问题标题】:XSLT3 and Xpath2: Best way to identify greatest element below a threshold in sorted sequence?XSLT3 和 Xpath2:识别排序序列中低于阈值的最大元素的最佳方法?
【发布时间】:2013-09-17 17:34:24
【问题描述】:

我是带有 Xpath2 的 XSLT3 的 Saxon 9.5EE 实现,我正在寻找最快的方法来识别排序序列 $seq 中小于某个给定值 $value 的最大元素。

据我所知,对于序列来说,没有任何东西相当于“preceding::sibling”。这意味着 Xpath 在遍历序列时不如遍历 XML 树时敏捷。

也就是说,你不能说$seq[。 gt $value][1]/preceding-sibling:item[1] 因为“/”只为节点定义,“preceding-sibling”不会引用序列,而是引用相关节点所在的 XML 树。

无论如何...

我找到了两种方法,但它们似乎不必要地复杂。

一种方法是:

$seq[($seq!(if(. gt $value) then position() else ()))[1] - 1]

另一种方法是

<xsl:iterate select="$seq">
    <xsl:variable name="pos" select="position()"/>
<xsl:if test=". gt $trial">
    <xsl:text>
</xsl:text>
    <xsl:sequence select="$seq[$pos - 1]"/>
    <xsl:break/>
</xsl:if>
</xsl:iterate>

有没有更好的办法?

顺便说一句,测试这两个选项得到了有趣的结果。如果我只是在寻找相关项目的位置,那么它们最终在性能上几乎相同。

但是,如果我真的对值本身感兴趣,那么该选项会压垮另一个……大概是因为由于迭代命令,序列已经在内存中准备好了。

【问题讨论】:

    标签: xslt xpath saxon xpath-2.0


    【解决方案1】:

    怎么样:

    $seq[. lt $value][last()]
    

    【讨论】:

    • 从表面上看,它的缺点是它没有考虑序列排序的知识;如果所需的项目接近开头,则将扫描整个序列。
    • 我测试了这个算法,结果似乎很大程度上取决于 XSL 的实现(即底层 XSL 处理器的工作方式)。在没有字节码生成的情况下,这种方法看起来与 Saxon 实现中的迭代中断算法一样好。使用字节码生成,这个算法看起来差了 2 到 3 倍。
    【解决方案2】:

    我认为递归函数是最优雅的解决方案:

    function f ($seq, $value, $bestSoFar) {
       if (head($seq) > $value)
       then $bestSoFar
       else f(tail($seq), $value, head($seq))
    }
    

    【讨论】:

    • 谢谢,迈克尔。我很难让它在 oXygen 的 Saxon EE 中作为内联递归表达式工作,特别是它告诉我 $f 没有被声明。但是,这可能与说服 Saxon 9.5.2 使用所有增强功能的已知问题有关。我将尝试 oXygen 的测试版 Saxon 9.5.1.1 版本。
    • 内联函数不容易递归,因为变量不在其自己的初始化程序的范围内。有一种称为“Y-combinators”的技术可以解决这个问题,但它不适合胆小的人。
    【解决方案3】:

    如果您的实现将序列存储为数组,最快的方法是使用二分搜索 O(log n),而不是像其他答案的线性搜索一样使用 O(n)

    如果您的实现可以通过保留范围引用来计算 O(1) 中的子序列,您可以使用:

     function f ($seq, $value, $best) { 
        if (count($seq) = 0) then $best
        else let $midi := (count($seq) + 1) idiv 2
        return if ($seq[$midi] <= $value) then f(subsequence($seq, $midi + 1), $value, $seq[$midi])  
        else f(subsequence($seq, 1, $midi - 1), $value, $best)
      }
    

    否则可以将子序列保留为函数参数:

     function f ($seq, $first, $last, $value, $best) { 
        if ($last < $first) then $best
        else let $midi := $first + ($last - $first) idiv 2
        return if ($seq[$midi] <= $value) then f($seq, $midi + 1, $last, $value, $seq[$midi])  
        else f($seq, $first, $midi - 1, $value, $best)
      }
      function call-f($seq, $value) {
        f($seq, 1, count($seq), $value, ())
      }
    

    【讨论】:

      猜你喜欢
      • 2017-10-07
      • 2021-01-05
      • 2014-05-12
      • 1970-01-01
      • 2011-01-15
      • 2014-11-09
      • 2015-02-25
      • 1970-01-01
      • 2023-03-25
      相关资源
      最近更新 更多