</xsl:choose>
</xsl:template>
Вызвав этот шаблон с параметром n
равным 6
следующим образом:
<xsl:call-template name='factorial'>
<xsl:with-param name='n' select='number(6)'/>
</xsl:call-template>
мы получим текстовый узел, значение которого будет равно '720
'.
Очевидным требованием к рекурсивным функциям является возможность выхода из рекурсии. Если бы в определении факториала не было указано, что 0!=1, вычисления так бы и продолжались без конца.
Главным минусом рекурсии является требовательность к ресурсам. Каждый раз, при вызове именованного шаблона, процессор должен будет каким-то образом сохранять в памяти передаваемые ему формальные параметры. Например, если мы попробуем сосчитать факториал от 170, процессору понадобится держать в памяти сразу 170 чисел. Безусловно, в случае с факториалом это не является большой проблемой — точность 64-битных чисел исчерпается гораздо раньше, чем закончится память, но в случае хранения в переменных действительно больших объемов информации (например, частей деревьев) такая угроза существует. Кроме того, рекурсивные решения, как правило, работают медленнее, чем решения, не использующие рекурсию.
Так в чем же смысл использования рекурсии? Дело в том, что вследствие определенных ограничений (связанных, в частности с неизменяемыми переменными) в XSLT существуют задачи, которые не могут быть реализованы иначе кроме как через рекурсию. Самым характерным примером такой задачи являются циклы.
Циклы
Цикл в общем смысле слова это повторение одних и тех же действий несколько раз. Если говорить об 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' :=
х2' :=
...
xn' :=
x1 := x1'
x2 := x2'
...
xn := xn'
иначе
вернуть
Переопределение значений переменных x
1, … , х
n в этом случае выполняют n
функций:
1 …,
n. И если изменить значение переменной мы не могли, переопределить связанное с ней значение мы вполне в состоянии, добавив в контекст новый параметр или переменную с тем же именем.
Теперь мы можем записать весь цикл while
как одну рекурсию:
while(x1, ..., xn) ::=
если
выполняется