Частным случаем цикла while является цикл for. В разных языках программирования for имеет различную семантику; мы будем рассматривать циклы for вида
for (int i = 0; i < n; i++) { ... }
в языках Java и C или
for i := 0 to n-1 do begin ... end;
в Pascal. Иными словами, нас будет интересовать циклическое выполнение определённых действий при изменении значения некоторой переменной (называемой иногда индексом цикла) в интервале целых чисел от 0 до n включительно.
Цикл for может быть определён через while с использованием следующих условных и изменяющих функций:
условие($i,$n,$x1,...,$xk) ::= ($i < $n) функцияi($i,$n,$x1,...,$xk) ::= ($i + 1) функцияn($i,$n,$x1,...,$xk) ::= ($n)
Шаблон цикла for в общем виде будет выглядеть как:
Пример 11.12. Шаблон цикла for в общем виде
<xsl:template name="for">
<xsl:param name="i" select="0"/>
<xsl:param name="n"/>
<!-- Другие переменные -->
<xsl:param name="x1"/>
<!-- ... -->
<xsl:param name="xk"/>
<xsl:choose>
<xsl:when test="$i < $n">
<!-- Действия -->
<xsl:call-template name="for">
<xsl:with-param name="i" select="$i + 1"/>
<xsl:with-param name="n" select="$n"/>
<!-- Другие переменные -->
<xsl:with-param name="x1" select="функция1($i,$n,$x1,...$xk)"/>
<!-- ... -->
<xsl:with-param name="xk" select="функцияk($i,$n,$x1,...$xk)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="результат($i,$n,$x1,...,$xk)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
В качестве примера цикла for приведём шаблон, вычисляющий n первых чисел Фибоначчи.
Числа Фибоначчи — это рекуррентная последовательность вида
1 1 2 3 5 8 13 21 ...
и так далее, где каждое последующее число определяется как сумма двух предыдущих.
Для вычисления n первых чисел Фибоначчи мы можем использовать две переменные current и last, соответствующих текущему числу и числу, полученному на предыдущем шаге соответственно. Функции, переопределяющие эти переменные совершенно очевидны:
функцияlast($i,$n,$last,$current) ::= ($current) функцияcurrent($i,$n,$last,$current) ::= ($current + $last)
Поскольку в данном случае нам не нужно возвращать результат, нам нужно лишь циклически выводить очередное число Фибоначчи, шаблон for может быть немного упрощён использованием элемента xsl:if вместо xsl:choose
Пример 11.13. Шаблон, вычисляющий числа Фибоначчи
<xsl:template name="for">
<xsl:param name="i" select="0"/>
<xsl:param name="n"/>
<xsl:param name="last" select="0"/>
<xsl:param name="current" select="1"/>
<xsl:if test="$i < $n">
<xsl:text> </xsl:text>
<xsl:value-of select="$current"/>
<xsl:call-template name="for">
<xsl:with-param name="i" select="$i + 1"/>
<xsl:with-param name="n" select="$n"/>
<xsl:with-param name="last" select="$current"/>
<xsl:with-param name="current" select="$last + $current"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Вызванный в основном шаблоне как:
<xsl:template match="/">
<xsl:call-template name="for">
<xsl:with-param name="n" select="6"/>
</xsl:call-template>
</xsl:template>
этот шаблон создаст в выходящем документе последовательность:
1 1 2 3 5 8
Приведём ещё более простой пример, в котором элемент option выводится заданное число раз:
Пример 11.14. Вывод 10 элементов option
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:call-template name="for">
<xsl:with-param name="n" select="10"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="for">
<xsl:param name="i" select="0"/>
<xsl:param name="n"/>
<xsl:if test="$i < $n">
<option>
<xsl:value-of select="$i"/>
</option>
<xsl:call-template name="for">
<xsl:with-param name="i" select="$i + 1"/>
<xsl:with-param name="n" select="$n"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Пример 11.15. Выходящий документ
<option>0</option> <option>1</option> <option>2</option> <option>3</option> <option>4</option> <option>5</option> <option>6</option> <option>7</option> <option>8</option> <option>9</option>
Пожалуй, этим примером мы и закончим рассмотрение рекурсии. Осталось лишь добавить, что при всей своей простоте и вычислительной мощи, рекурсия является гораздо более требовательной к ресурсам техникой программирования, чем обычная итеративная обработка. Поэтому всегда следует тщательно оценивать, во что может вылиться использование рекурсии. В любом случае следует избегать глубоких рекурсий (функций, количество рекурсивных вызовов в которых может быть большим) и рекурсий, неэкономно использующих память.
Кроме того, большинство действий, выполнение которых в XSLT затруднено, в классических языках программирования выполняется, как правило, намного легче и эффективней. Поэтому, каждый раз, когда стоит вопрос об использовании рекурсии, наряду с ней следует рассматривать такую альтернативу, как использование расширений XSLT, написанных на обычном императивном языке.