Цикл в общем смысле слова — это повторение одних и тех же действий несколько раз. Если говорить об XSLT, то цикл — это многократное выполнение одного и того же шаблона. Для подавляющего большинства случаев в преобразованиях достаточно бывает использовать такие элементы, как xsl:apply-templates и xsl:for-each, которые заставляют процессор выполнять одни и те же действия несколько раз в контексте каждого из узлов определённого множества.
Весомым ограничением такого рода циклической обработки является невозможность генерировать множества узлов. В текущей версии языка никакой другой тип не может быть приведён ко множеству узлов, значит, в любое из них могут входить только те узлы, которые изначально присутствуют в одном из обрабатываемых документов. Это означает, что ни xsl:apply-templates, ни xsl:for-each не могут быть использованы для того, чтобы реализовать простые while- или for-циклы для произвольных множеств.
Наиболее примитивной циклической конструкцией во многих языках программирования является цикл while (англ. пока). Цикл while, как правило, имеет следующий вид:
пока верно условие выполнять действия
В качестве примера while-цикла напишем на языке Java программу вычисления факториала в итеративном стиле:
int factorial(int n)
{
int i = n;
int result = 1;
while (i != 0)
{
result = result * i;
i--;
}
return result;
}
В этой функции условием является отличие значения переменной i от 0, а действиями — умножение значения переменной result на значение переменной i, и уменьшение значения этой переменной на 1.
Цикл while не может быть запрограммирован в XSLT итеративно потому как действия, как правило, изменяют значения переменных, в контексте которых вычисляется условие, определяющее, продолжать выполнение цикла или нет. Дадим другую общую запись цикла while, выделив изменение переменных:
пока верно условие(x1,x2,...,xn) выполнить x1' := функция1(x1,x2,...,xn) x2' := функция2(x1,x2,...,xn) ... xn' := функцияn(x1,x2,...,xn) действия(x1,x2,...,xn) x1 := x1' x2 := x2' ... xn := xn' иначе вернуть результат(x1,...,xn)
Переопределение значений переменных x1,...,xn в этом случае выполняют n функций: функция1,...,функцияn. И если изменить значение переменной мы не могли, переопределить связанное с ней в значение мы вполне в состоянии, добавив в контекст новый параметр или переменную с тем же именем.
Теперь мы можем записать весь цикл while как одну рекурсию:
while(x1,...,xn) ::=
если
выполняется условие(x1,...,xn)
то
действия(x1,...,xn)
while(функция1(x1,...,xn),
функция2(x1,...,xn),
...,
функцияn(x1,...,xn))
иначе
результат(x1,...,xn)
Теперь уже совершенно очевидно, как while-цикл должен выглядеть в преобразовании:
Пример 11.10. Шаблон цикла while в общем виде
<xsl:template name="while">
<xsl:param name="x1"/>
<!-- ... -->
<xsl:param name="xn"/>
<xsl:choose>
<xsl:when test="условие($x1,...,$xn)">
<!-- Действия -->
<xsl:call-template name="while">
<xsl:with-param name="x1" select="функция1($x1,...$xn)"/>
<!-- ... -->
<xsl:with-param name="xn" select="функцияn($x1,...$xn)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="результат($x1,...,$xn)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
В качестве примера приведём while-цикл для программы, вычисляющей факториал. Java-код был следующим:
while (i != 0)
{
result = result * i;
i--;
}
В этом цикле участвуют две переменные — i и result. Функции, использующиеся в этом цикле, запишутся следующим образом:
условие($i, $result) ::= ($i != 0) функцияi($i, $result) ::= ($i - 1) функцияresult($i, $result) ::= ($i * $result) результат($I, $result) ::= ($result)
Именованный шаблон для этого случая будет иметь вид:
Пример 11.11. Пример шаблона цикла while
<xsl:template name="while">
<xsl:param name="i"/>
<xsl:param name="result"/>
<xsl:choose>
<xsl:when test="$i != 0">
<xsl:call-template name="while">
<xsl:with-param name="i" select="$i - 1"/>
<xsl:with-param name="result" select="$result * $i"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Вызвать этот шаблон можно следующим образом:
<xsl:template match="/">
<xsl:call-template name="while">
<xsl:with-param name="i" select="6"/>
<xsl:with-param name="result" select="1"/>
</xsl:call-template>
</xsl:template>
Результатом будет, естественно, число 720.