Алексей Валиков - Технология XSLT
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)
х2' := функция2(x1,x2,...,xn)
...
xn' := функцияn(x1,x2,...,xn)
действия(x1,x2,...,хn)
x1 := x1'
x2 := x2'
...
xn := xn'
иначе
вернуть результат(x1,...,хn)
Переопределение значений переменных x1, … , хn в этом случае выполняют n функций: функция1 …, функцияn. И если изменить значение переменной мы не могли, переопределить связанное с ней значение мы вполне в состоянии, добавив в контекст новый параметр или переменную с тем же именем.
Теперь мы можем записать весь цикл while как одну рекурсию:
while(x1, ..., xn) ::=
если
выполняется условие(x1, ..., xn)
то
действия(x1, ..., хn)
while(функция1(x1, ..., хn),
функция2(x1, ..., хn),
...,
функцияn(x1, ..., xn))
иначе
результат(x1, ..., хn)
Теперь уже совершенно очевидно, как 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. Функции, использующиеся в этом цикле, запишутся следующим образом:
условие($1, $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.
Цикл for
Частным случаем цикла while является цикл for. В разных языках программирования for имеет различную семантику; мы будем рассматривать циклы for вида
for (int i = 0; i < n; i++) { ... }
в языках Java и С или
for i := 0 to n-1 do begin ... end;
в Pascal. Иными словами, нас будет интересовать циклическое выполнение определенных действий при изменении значения некоторой переменной (называемой иногда индексом цикла) в интервале целых чисел от 0 до n включительно.
Цикл for может быть определен через while с использованием следующих условных и изменяющих функций:
условие($i, $n,$x1,...,$хk) :: = ($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"/>