xsl:iterate

xsl:iterate and Tail-Recursive Style

Variables never change, so a running total once needed a template calling itself; processors turn such tail calls into loops, but other recursion overflows the stack on long input. xsl:iterate writes the loop directly: xsl:param holds state, xsl:next-iteration passes it on, xsl:break stops early, and xsl:on-completion runs at the end. It is streamable (XSLT Analysis and Streaming). Run xslt3 -s:booknest-catalog.xml -xsl:iterate.xsl:

iterate.xsl: a running total with an early exitXML
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" expand-text="yes">
  <xsl:output method="text"/>
  <xsl:param name="budget" as="xs:decimal" select="70"/>
  <xsl:template match="/catalog">
    <xsl:iterate select="book">
      <xsl:param name="total" as="xs:decimal" select="0"/>
      <xsl:on-completion>All books fit: {$total}&#10;</xsl:on-completion>
      <xsl:variable name="new" select="$total + xs:decimal(.//price)"/>
      <xsl:choose>
        <xsl:when test="$new gt $budget">
          <xsl:break>Stop at {@id}: {$total}&#10;</xsl:break></xsl:when>
        <xsl:otherwise>{@id} +{.//price} = {$new}&#10;<xsl:next-iteration>
          <xsl:with-param name="total" select="$new"/></xsl:next-iteration></xsl:otherwise>
      </xsl:choose>
    </xsl:iterate>
  </xsl:template>
</xsl:stylesheet>
Output
b1 +14.99 = 14.99
b2 +39.50 = 54.49
Stop at b3: 54.49

With budget=200 the loop ends in xsl:on-completion: All books fit: 134.74. It has no context item: a draft that counted orders inside it failed with XPDY0002.