Выбрать главу

условие($i, $n,$x1,...,$хk)      :: = ($i < $n)

функцияi($i, $n, $x1, ... , $xk) ::= ($i + 1)

функцияn($i, $n, $x1, ..., $xk)  :: = ($n)

Шаблон цикла for в общем виде будет выглядеть как.

Листинг 11.12. Шаблон цикла for в общем виде

<xsclass="underline" template name="for">

 <xsclass="underline" param name="i" select="0"/>

 <xsclass="underline" param name="n"/>

 <!-- Другие переменные -->

 <xsclass="underline" param name="x1"/>

 <!-- ... -->

 <xsclass="underline" param name="xk"/>

 <xsclass="underline" choose>

  <xsclass="underline" when test="$i &lt; $n">

   <!-- Действия -->

   <xsclass="underline" call-template name="for">

    <xsclass="underline" with-param name="i" select="$i + 1"/>

    <xsclass="underline" with-param name="n" select="$n"/>

    <!-- Другие переменные -->

    <xsclass="underline" with-param" name="x1" select функция1($i, $n, $x1, ..., $xk) "/>

    <!-- ... -->

    <xsclass="underline" with-param name="xk" select="функцияk($i, $n, $x1, ..., $xk)"/>

   </xsclass="underline" call-template>

  </xsclass="underline" when>

  <xsclass="underline" otherwise>

   <xsclass="underline" value-of select="результат($i,$n,$x1,...,$xk)"/>

  </xsclass="underline" otherwise>

 </xsclass="underline" choose>

</xsclass="underline" 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 может быть немного упрощен использованием элемента xsclass="underline" if вместо xsclass="underline" choose.

Листинг 11.13. Шаблон, вычисляющий числа Фибоначчи

<xsclass="underline" template name="for">

 <xsclass="underline" param name="i" select="0"/>

 <xsclass="underline" param name="n"/>

 <xsclass="underline" param name="last" select="0"/>

 <xsclass="underline" param name="current" select="1"/>

 <xsclass="underline" if test="$i &lt; $n">

  <xsclass="underline" text> </xsclass="underline" text>

  <xsclass="underline" value-of select="$current"/>

  <xsclass="underline" call-template name="for">

   <xsclass="underline" with-param name="i" select="$i + 1"/>

   <xsclass="underline" with-param name="n" select="$n"/>

   <xsclass="underline" with-param name="last" select="$current"/>

   <xsclass="underline" with-param name="current" select="$last + $current"/>

  </xsclass="underline" call-template>

 </xsclass="underline" if>

/xsclass="underline" template>

Вызванный в основном шаблоне как:

<xsclass="underline" template match="/">

 <xsclass="underline" call-template name="for">

  <xsclass="underline" with-param name="n" select="6"/>

 </xsclass="underline" call-template>

</xsclass="underline" template>

этот шаблон создаст в выходящем документе последовательность:

1 1 2 3 5 8

Приведем еще более простой пример, в котором элемент option выводится заданное число раз.

Листинг 11.14. Вывод 10 элементов option

<xsclass="underline" stylesheet

 version="1.0"

 xmlns:xsl="http://www.w3.org/1999/XSL/Transform'">

 <xsclass="underline" template match="/">

  <xsclass="underline" call-template name="for">

   <xsclass="underline" with-param name="n" select="10"/>

  </xsclass="underline" call-template>

 </xsclass="underline" template>

 <xsclass="underline" template name="for">

  <xsclass="underline" param name="i" select="0"/>

  <xsclass="underline" param name="n"/>

  <xsclass="underline" if test="$i &lt; $n">

   <option>

    <xsclass="underline" value-of select="$i"/>

   </option>

   <xsclass="underline" call-template name="for">

    <xsclass="underline" with-param name="i" select="$i + 1"/>

    <xsclass="underline" with-param name="n" select="$n"/>

   </xsclass="underline" call-template>

  </xsclass="underline" if>

 </xsclass="underline" template>

</xsclass="underline" 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, написанных на обычном императивном языке.

Метод Пиза для for-цикла

Для простых for-циклов, которые должны выполниться строго определенное число раз, вместо рекурсии можно использовать весьма остроумный метод, предложенный Венделлом Пизом (Wendell Piez, Mullberry Technologies, Inc). Суть метода состоит в том, что хоть мы и не можем сгенерировать множество узлов, выбрать множество с определенным количеством узлов нам вполне по силам.

Для начала выберем какое-нибудь множество узлов документа преобразования: