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

    <xsclass="underline" with-param name="y" select="1"/>

   </xsclass="underline" call-template>

  </xsclass="underline" variable>

  <xsclass="underline" variable name="less-than" select="boolean(number($result))"/>

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

 </xsclass="underline" template>

 <xsclass="underline" template name="less-than">

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

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

  <xsclass="underline" value-of select="number($x &lt; $y)"/>

 </xsclass="underline" template>

</xsclass="underline" stylesheet>

Пример

Простым примером шаблона-функции может быть шаблон, который форматирует дату в нужном виде, например 7 августа 93 года как "07-Aug-1993".

В качестве параметров этот шаблон будет принимать численные значения дня, месяца и года. Год, имеющий значение меньшее 25, мы будем считать принадлежащим новому тысячелетию.

Листинг 11.8. Шаблон, форматирующий дату

<xsclass="underline" template name="format-date">

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

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

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

 <xsclass="underline" value-of select="format-number($day, '00')"/>

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

 <xsclass="underline" choose>

  <xsclass="underline" when test="$month = 1">Jan</xsclass="underline" when>

  <xsclass="underline" when test="$month = 2">Feb</xsclass="underline" when>

  <xsclass="underline" when test="$month = 3">Mar</xsclass="underline" when>

  <xsclass="underline" when test="$month = 4">Apr</xsclass="underline" when>

  <xsclass="underline" when test="$month = 5">May</xsclass="underline" when>

  <xsclass="underline" when test="$month = 6">Jun</xsclass="underline" when>

  <xsclass="underline" when test="$month = 7">Jul</xsclass="underline" when>

  <xsclass="underline" when test="$month = 8">Aug</xsclass="underline" when>

  <xsclass="underline" when-test="$month = 9">Sen</xsclass="underline" when>

  <xsclass="underline" when test="$month = 10">Oct</xsclass="underline" when>

  <xsclass="underline" when test="$month = 11">Nov</xsclass="underline" when>

  <xsclass="underline" when test="$month = 12">Dec</xsclass="underline" when>

 </xsclass="underline" choose>

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

 <xsclass="underline" choose>

  <xsclass="underline" when test="$year &lt;= 25">

   <xsclass="underline" value-of select="format-number($year +2000, '0000')"/>

  </xsclass="underline" when>

  <xsclass="underline" otherwise>

   <xsclass="underline" value-of select="format-number($year, '0000')"/>

  </xsclass="underline" otherwise>

 </xsclass="underline" choose>

</xsclass="underline" template>

Рекурсия

Отсутствие в XSLT изменяемых переменных (оценим красоту этой тавтологии) как, впрочем, и многое другое, делает этот язык совершенно непохожим на многие классические языки программирования. В этом разделе мы опишем рекурсию [Кормен и др. 2000, Кнут 2000] — чрезвычайно простую, но в то же время исключительно мощную технику, которая в большинстве случаев компенсирует нехватку в XSLT переменных и других процедурных конструкций.

Не вдаваясь в строгие определения дискретной математики, можно сказать, что рекурсия это всего лишь описание объекта или вычисления в терминах самого себя. Пожалуй, самым простым примером рекурсии является факториал, функция, которая математически определяется как:

0!=1

n!=n×(n-1)!

Программа на процедурном языке (например, таком, как Java), вычисляющая факториал совершенно тривиальна:

int factorial(int n) {

 if (n == 0) return 1;

 else return n * factorial(n-1);

}

Попробуем запрограммировать факториал на XSLT. Мы уже научились создавать собственные функции (вернее, конструкции, похожие на них) с помощью одних только именованных шаблонов, значит написать функцию, которая бы вызывала сама себя, будет не так уж и сложно.

Листинг 11.9. Именованный шаблон, вычисляющий факториал

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

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

 <xsclass="underline" choose>

  <xsclass="underline" when test="$n=0">1</xsclass="underline" when>

  <xsclass="underline" otherwise>

   <xsclass="underline" variable name="n-1">

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

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

    </xsclass="underline" call-template>

   </xsclass="underline" variable>

   <xsclass="underline" value-of select="$n * number($n-1)"/>

  </xsclass="underline" otherwise>

 </xsclass="underline" choose>

</xsclass="underline" template>

Вызвав этот шаблон с параметром n равным 6 следующим образом:

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

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

</xsclass="underline" call-template>

мы получим текстовый узел, значение которого будет равно "720".

Очевидным требованием к рекурсивным функциям является возможность выхода из рекурсии. Если бы в определении факториала не было указано, что 0!=1, вычисления так бы и продолжались без конца.

Главным минусом рекурсии является требовательность к ресурсам. Каждый раз, при вызове именованного шаблона, процессор должен будет каким-то образом сохранять в памяти передаваемые ему формальные параметры. Например, если мы попробуем сосчитать факториал от 170, процессору понадобится держать в памяти сразу 170 чисел. Безусловно, в случае с факториалом это не является большой проблемой — точность 64-битных чисел исчерпается гораздо раньше, чем закончится память, но в случае хранения в переменных действительно больших объемов информации (например, частей деревьев) такая угроза существует. Кроме того, рекурсивные решения, как правило, работают медленнее, чем решения, не использующие рекурсию.

Так в чем же смысл использования рекурсии? Дело в том, что вследствие определенных ограничений (связанных, в частности с неизменяемыми переменными) в XSLT существуют задачи, которые не могут быть реализованы иначе кроме как через рекурсию. Самым характерным примером такой задачи являются циклы.

Циклы

Цикл в общем смысле слова это повторение одних и тех же действий несколько раз. Если говорить об XSLT, то цикл это многократное выполнение одного и того же шаблона. Для подавляющего большинства случаев в преобразованиях достаточно бывает использовать такие элементы, как xsclass="underline" apply-templates и xsclass="underline" for-each, которые заставляют процессор выполнять одни и те же действия несколько раз в контексте каждого из узлов определенного множества.

Весомым ограничением такого рода циклической обработки является невозможность генерировать множества узлов. В текущей версии языка никакой другой тип не может быть приведен ко множеству узлов, значит, в любое из них могут входить только те узлы, которые изначально присутствуют в одном из обрабатываемых документов. Это означает, что ни xsclass="underline" apply-templates, ни xsclass="underline" for-each не могут быть использованы для того, чтобы реализовать простые while- или for-циклы для произвольных множеств.