Saxonica.com

saxon:iterate

The saxon:iterate element is used to iterate over a sequence. In simple cases the effect is exactly the same as xsl:for-each. However, saxon:iterate offers a guarantee that the evaluation will be sequential. It allows each iteration to set parameters which will be available during the next iteration, and it allows early exit from the loop when some condition is true.

This extension is new in Saxon 9.1. It should be regarded as experimental. The primary motivation for introducing it is to enable streamed processing of input files where there is a need to "remember" data read at the beginning of the document for use when processing elements encountered later. Streaming is available only in Saxon-SA, but the basic functionality of saxon:iterate is also available in Saxon-B

The following example shows how a shopping basket may be displayed with the cumulative values of the items:


  <saxon:iterate select="//ITEM" xmlns:saxon="http://saxon.sf.net/" xsl:extension-element-prefixes="saxon">
    <xsl:param name="basketCost" as="xs:decimal" select="0"/>
    <item cost="{$basketCost}"><xsl:copy-of select="TITLE"/></item>
    <saxon:continue>
      <xsl:with-param name="basketCost" select="$basketCost + (xs:decimal(PRICE), 0)[1]"/>
    </saxon:continue>
  </saxon:iterate>

The initial values of the parameters are taken from the select attribute of the xsl:param elements; values for subsequent iterations are taken from the xsl:with-param in the saxon:continue instruction.

The following example shows early exit from the loop when the cumulative value reaches 25.00:


  <saxon:iterate select="//ITEM" xmlns:saxon="http://saxon.sf.net/" xsl:extension-element-prefixes="saxon">
    <xsl:param name="basketCost" as="xs:decimal" select="0"/>
    <xsl:choose>
      <xsl:when test="$basketCost gt 25.00">
        <saxon:break/>
      </xsl:when>
      <xsl:otherwise>
        <item cost="{$basketCost}"><xsl:copy-of select="TITLE"/></item>
        <saxon:continue>
          <xsl:with-param name="basketCost" select="$basketCost + (xs:decimal(PRICE), 0)[1]"/>
        </saxon:continue>
      </xsl:otherwise>
    </xsl:choose>  
  </saxon:iterate>  

The instructions saxon:continue and saxon:break must be lexically within the saxon:iterate instruction and must not be followed by further instructions other than xsl:fallback. They may appear within xsl:if or xsl:choose (nested to any depth) so long as none of the intermediate instructions have a following sibling instruction. They must not appear with a nested xsl:for-each, a nested literal result element, or nested inside any other instruction.

Within the body of the instruction, the context item, position, and size are available just as in xsl:for-each. The value of last() reflects the size of the input sequence, which may be greater than the number of iterations if saxon:break is used for early exit.

A saxon:finally instruction may be included as the last child of saxon:iterate; it is executed when the end of the input sequence is reached. It is not evaluated if the loop is terminated using saxon:break. The focus (context node, position, and size) for this instruction is undefined; however, variables declared within the loop including the loop parameters are available for reference. The loop parameters have their values as set by the saxon:continue instruction that ended the last normal iteration.

This instruction is similar in form to a tail-recursive function, but many users may find it easier to code. It is also easier to optimize. The main difference from a tail-recursive function is that the context item is available to refer to the the item being processed in each iteration, and there is no need to explicitly find the tail of the sequence when evaluating parameters for the next iteration, nor to test whether the sequence is empty in order to terminate the iteration.

Next