Building jxom stylesheets I've learned what is a "good" and "bad" recursion from the saxon's perspective.
I'm using control tokens $t:indent and $t:unindent to control indentation in the sequence of tokens defining java output. To build output lines I need to calculate total indentation for each line. This can be done using cummulative sum, considering $t:indent as +1 and $t:unindent as -1.
This task can be formalized as "calculate cummulative integer sum".
The first approach I've tested is non recursive: "for $i in 1 to count($items) return sum(subsequence($items, 1, $i))".It is incredibly slow.
The next try was recurrent: calculate and spew results as they are calculated.This is "crash fast" method. Saxon, indeed, implements this as recursion and arrives to a stack limit early.
The last approach, employes saxon's ability to detect some particular flavour of tail calls. When function contains a tail call, and the output on a tail call code path consists of this tail call only, then saxon transforms such construction into a cycle. Thus I need to accumulate result and pass it down to a tail call chain and output it on the last opportunity only.
The following sample shows this technique:
<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:t="http://www.nesterovsky-bros.com" exclude-result-prefixes="xs t"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <xsl:variable name="values" as="xs:integer*" select="1 to 10000"/> <result> <sum> <xsl:value-of select="t:cumulative-integer-sum($values)"/> <!-- This call crashes with stack overflow. --> <!-- <xsl:value-of select="t:bad-cumulative-integer-sum($values)"/> --> <!-- To compare speed uncomment following lines. --> <!--<xsl:value-of select="sum(t:cumulative-integer-sum($values))"/>--> <!--<xsl:value-of select="sum(t:slow-cumulative-integer-sum($values))"/>--> </sum> </result> </xsl:template> <!-- Calculates cumulative sum of integer sequence. $items - input integer sequence. Returns an integer sequence that is a cumulative sum of original sequence. --> <xsl:function name="t:cumulative-integer-sum" as="xs:integer*"> <xsl:param name="items" as="xs:integer*"/> <xsl:sequence select="t:cumulative-integer-sum-impl($items, 1, 0, ())"/> </xsl:function> <!-- Implementation of the t:cumulative-integer-sum. $items - input integer sequence. $index - current iteration index. $sum - base sum. $result - collected result. Returns an integer sequence that is a cumulative sum of original sequence. --> <xsl:function name="t:cumulative-integer-sum-impl" as="xs:integer*"> <xsl:param name="items" as="xs:integer*"/> <xsl:param name="index" as="xs:integer"/> <xsl:param name="sum" as="xs:integer"/> <xsl:param name="result" as="xs:integer*"/> <xsl:variable name="item" as="xs:integer?" select="$items[$index]"/> <xsl:choose> <xsl:when test="empty($item)"> <xsl:sequence select="$result"/> </xsl:when> <xsl:otherwise> <xsl:variable name="value" as="xs:integer" select="$item + $sum"/> <xsl:variable name="next" as="xs:integer+" select="$result, $value"/> <xsl:sequence select=" t:cumulative-integer-sum-impl($items, $index + 1, $value, $next)"/> </xsl:otherwise> </xsl:choose> </xsl:function> <!-- "Bad" implementation of the cumulative-integer-sum. --> <xsl:function name="t:bad-cumulative-integer-sum" as="xs:integer*"> <xsl:param name="items" as="xs:integer*"/> <xsl:sequence select="t:bad-cumulative-integer-sum-impl($items, 1, 0)"/> </xsl:function> <!-- "Bad" implementation of the cumulative-integer-sum. --> <xsl:function name="t:bad-cumulative-integer-sum-impl" as="xs:integer*"> <xsl:param name="items" as="xs:integer*"/> <xsl:param name="index" as="xs:integer"/> <xsl:param name="sum" as="xs:integer"/> <xsl:variable name="item" as="xs:integer?" select="$items[$index]"/> <xsl:if test="exists($item)"> <xsl:variable name="value" as="xs:integer" select="$item + $sum"/> <xsl:sequence select="$value"/> <xsl:sequence select=" t:bad-cumulative-integer-sum-impl($items, $index + 1, $value)"/> </xsl:if> </xsl:function> <!-- Non recursive implementation of the cumulative-integer-sum. --> <xsl:function name="t:slow-cumulative-integer-sum" as="xs:integer*"> <xsl:param name="items" as="xs:integer*"/> <xsl:sequence select=" for $i in 1 to count($items) return sum(subsequence($items, 1, $i))"/> </xsl:function></xsl:stylesheet>