【问题标题】:How could I optimize the XPath test not(previous-sibling::sect1) for testing whether this is the first sect1 child element?我如何优化 XPath 测试 not(previous-sibling::sect1) 以测试这是否是第一个 sect1 子元素?
【发布时间】:2010-08-27 13:32:50
【问题描述】:

我正在开发一个 XSLT 1.0 样式表(并使用 xsltproc 应用它)。我脚本中的一个模板应该对给定父节点中的第一个 <sect1> 元素和最后一个 <sect1> 元素执行一些特殊处理。现在这种特殊处理是这样实现的:

<xsl:template match="sect1">
  <xsl:if test="not(preceding-sibling::sect1)">
    <!-- Special handling for first sect1 element goes here. -->
  </xsl:if>
  <!-- Common handling for all sect1 elements goes here. -->
  <xsl:if test="not(following-sibling::sect1)">
    <!-- Special handling for last sect1 element goes here. -->
  </xsl:if>
</xsl:template>

我想知道(只是出于好奇,脚本的运行速度对我来说很好):有没有更有效的方法来做到这一点? XSLT 处理器是否可能会在第一次找到匹配项后停止组装 preceding-sibling::sect1 节点集,因为它知道它只需要找到一个或零个元素?

【问题讨论】:

    标签: optimization xslt xpath


    【解决方案1】:

    XSLT 处理器是否可能 将停止组装 前兄弟::sect1 节点集 在第一个找到的匹配之后,因为它 知道它只需要找到一个 还是零元素?

    我不了解 xsltproc,但 Saxon 非常擅长这类优化。我相信它只会检查第一个找到的匹配项,因为它只需要知道节点集是否为空。

    但是,您始终可以通过如下更改测试来确保:

      <xsl:if test="not(preceding-sibling::sect1[1])">
    

      <xsl:if test="not(following-sibling::sect1[1])">
    

    因为这只会测试每个轴上的第一个兄弟。请注意,每种情况下的 [1] 指的是 XPath 步骤的顺序,即轴的顺序,不一定是文档顺序。所以preceding-sibling::sect1[1] 指的是紧接在当前元素之前的 sect1 兄弟,而不是文档顺序中的第一个 sect1 兄弟。因为preceding-sibling轴的方向是反向的。

    【讨论】:

    • 是的,特定的 XSLT 处理器优化可以将此作为捷径,但来自 w3.org/TR/xpath/#predicates A predicate filters a node-set with respect to an axis to produce a new node-set 和来自 w3.org/TR/xpath/#axes the following-sibling axis contains all the following siblings of the context node 尚不清楚这是您可以信任的优化.在这种情况下,以及其他情况下,模式匹配是最好的方法。
    • @Alejandro:我同意,在不知道正在使用什么处理器的情况下,不能保证这种优化会发生。然而,OP 询问优化的可能性,我相信撒克逊人的可能性很高。您从规范中引用的部分指定了语义,而不是实现。即使“后续兄弟轴包含上下文节点的所有后续兄弟”,这并不意味着处理器必须实例化或处理所有这些兄弟才能符合要求。
    【解决方案2】:

    假设调用模板的上下文是子节点选择,那么我提供以下内容。如果他们被调用的上下文是通过不同的轴(比如前兄弟或祖先),那么接近它的方法是最好的。

    两种可能性是简化测试,或者用不同的模板替换它们:

    更简单的测试:

    <xsl:template match="sect1">
      <xsl:if test="position() = 1">
        <!-- Special handling for first sect1 element goes here. -->
      </xsl:if>
      <!-- Common handling for all sect1 elements goes here. -->
      <xsl:if test="position() = last()">
        <!-- Special handling for last sect1 element goes here. -->
      </xsl:if>
    </xsl:template>
    

    不同的模板:

    <xsl:template name="handleSect1">
      <!-- Common handling for all sect1 elements goes here. -->
    <xsl:template>
    <xsl:template match="sect1">
      <xsl:call-template name="handleSect1"/>
    </xsl:template>
    <xsl:template match="sect1[1]">
      <!-- Special handling for first sect1 element goes here. -->
      <xsl:call-template name="handleSect1"/>
    </xsl:template>
    <xsl:template match="sect1[last()]">
      <xsl:call-template name="handleSect1"/>
      <!-- Special handling for last sect1 element goes here. -->
    </xsl:template>
    <xsl:template match="sect1[position() = 1 and position() = last()]">
      <!-- Special handling for first sect1 element goes here. -->
      <xsl:call-template name="handleSect1"/>
      <!-- Special handling for last sect1 element goes here. -->
    </xsl:template>
    

    既然您说“优化”,我假设您关心哪个处理速度更快。它会根据 xslt 处理器、处理模式(有些具有“编译”选项,这将影响哪个更有效)和输入 XML 的不同而有所不同。最快的可能是这些或您的原始版本。

    真的,每一个都应该和另一个一样高效,区别在于处理器设法进行的优化。

    在这种情况下,我会在我的答案中支持第一个,因为它是最简洁的,但如果我不希望在所有 4 个案例之间共享共同处理,我会支持第二个答案中的方法,这很明显为每种情况标记不同的方法。

    【讨论】:

    • 第一个建议真的正确吗?仅当 sect1 元素是第一个子元素时, position() 不返回 1 吗?我希望为第一个 sect1 元素执行代码,即使它不是第一个子元素。
    • 它返回给定上下文中的位置。如果上下文使得其他节点可以继续,那么测试可以是“../sect1[1] = current()”。
    • 我认为你真的不需要单独的命名模板,你可以在匹配“sect1”的规则中添加@name。还要匹配第一个和最后一个规则,以避免规则中的重复代码只匹配一次(所以,第一个和最后一个)。关于您的最后一条评论:要测试您应该使用generate-id(../sect1[1]) = generate-id(current()) 的身份。
    • 最后,第一个建议不仅适用于子轴模板应用,还需要元素测试,如apply-templates select="sect1"
    【解决方案3】:

    我认为你应该能够做到

      <xsl:if test="position() = 1">
        <!-- Special handling for first sect1 element goes here. -->
      </xsl:if>
      <!-- Common handling for all sect1 elements goes here. -->
      <xsl:if test="position() = last()">
        <!-- Special handling for last sect1 element goes here. -->
      </xsl:if>
    

    因为position()last() 是上下文相关的。

    【讨论】:

    • 这不是二次复杂度,因为 position() 是(可能?)O(n) 函数?
    • 我刚刚意识到:这是不等价的:position() 返回 所有 个子项中的 position()。我只想知道它是否是第一个sect1 元素,而不是第一个子元素。
    • 这取决于它的使用方式。我认为在匹配模式中或仅在 for-each 循环中,是的 position() 和 last() 与上下文列表相关。但是,在其他地方,例如 XPath 表达式中的“/”之后,上下文是不同的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-04
    • 1970-01-01
    相关资源
    最近更新 更多