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

При реализации рекурсии в настоящем языке программирования создается функция — например, factorial, которая вызывается со значением 6: factorial(6). Факториал 6 вычисляется как 6 * factorial(5), поэтому функции нужно лишь умножить на 6 результат вызова самой себя со значением 5, то есть factorial(5). 

Далее, factorial(5) — это 5*factorial(4), поэтому функция снова вызывает сама себя, чтобы вычислить значение factorial(4). Этот процесс продолжается до вычисления factorial(1), а мы знаем, что 1! — это просто 1, поэтому factorial(1) возвращает 1. С этого момента управление последовательно возвращается на все предыдущие этапы, в результате чего будет вычислено выражение 1*2*3*4*5*6, или 720, что составляет 6!.

Кажется, что в таком языке стилей, как XSLT, реализовать подобное невозможно. Тем не менее, это можно сделать, по крайней мере, в XSLT 1.0. Основная идея состоит в том, что значение, возвращаемое шаблоном, можно сохранять в переменной, если шаблон вызывается внутри элемента <xsclass="underline" variable>, в котором объявляется эта переменная. Пусть, например, у нас есть именованный шаблон factorial, и мы хотим вычислить 6!. Тогда шаблону можно передать значение 6 при помощи элемента <xsclass="underline" with-param> и присвоить строковое значение результата переменной result, которое я затем показываю:

<?xml version="1.0"?>

<xsclass="underline" stylesheet version="1.0"

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

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

  <xsclass="underline" variable name="result">

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

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

   </xsclass="underline" call-template>

  </xsclass="underline" variable>

  6! = <xsclass="underline" value-of select="$result"/>

 </xsclass="underline" template>

 .

 .

 .

Следующий пример демонстрирует, как можно реализовать шаблон factorial, чтобы для вычисления факториала он вызывал сам себя. На языке программирования я мог бы написать рекурсивный вызов как n!=n*factorial(n-1), но у нас нет оператора присваивания; поэтому, когда я вычисляю factorial(n-1), я сохраняю это значение в новой переменной temp и на каждом шаге возвращаю значение n*$temp:

<?xml version="1.0"?>

<xsclass="underline" stylesheet version="1.0"

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

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

  <xsclass="underline" variable name="result">

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

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

   </xsclass="underline" call-template>

  </xsclass="underline" variable>

  6! = <xsclass="underline" value-of select="$result"/>

 </xsclass="underline" template>

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

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

  <xsclass="underline" choose>

   <xsclass="underline" when test="$value=1">

    <xsclass="underline" value-of select="1"/>

   </xsclass="underline" when>

   <xsclass="underline" otherwise>

    <xsclass="underline" variable name="temp">

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

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

     </xsclass="underline" call-template>

    </xsclass="underline" variable>

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

   </xsclass="underline" otherwise>

  </xsclass="underline" choose>

 </xsclass="underline" template>

</xsclass="underline" stylesheet>

Вот результирующий документ:

<?xml version="1.0" encoding="utf-8"?>

6! = 720

Как видите, это можно сделать, по крайней мере, в XSLT 1.0, в котором разрешены использованные здесь фрагменты результирующего дерева.

Шаблон: значение по умолчанию

Как я говорил ранее, в случае задания параметру значения при объявлении, оно может быть перекрыто, если вы зададите другое значение в элементе <xsclass="underline" with-param>. Но если другого значения не указывать, исходное значение выступит в роли значения по умолчанию.

Следующий пример видоизменяет рассмотренный ранее пример «COLORS». Шаблон имеет параметр COLOR, но я могу вызвать шаблон, не устанавливая COLOR в какое-либо определенное значение:

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

 <xsclass="underline" if test="NAME='Mercury'">

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

   <xsclass="underline" with-param name="COLOR" select="'RED'"/>

  </xsclass="underline" call-template>

 </xsclass="underline" if>

 <xsclass="underline" if test="NAME='Venus'">

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

   <xsclass="underline" with-param name="COLOR" select="'GREEN'"/>

  </xsclass="underline" call-template>

 </xsclass="underline" if>

 <xsclass="underline" if test="NAME='Earth'">

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

  </xsclass="underline" call-template>

 </xsclass="underline" if>

</xsclass="underline" template>

В этом случае параметр COLOR принимает значение по умолчанию«blue» (голубой), заданное в элементе <xsclass="underline" param> в шаблоне «COLORS»:

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

 <xsclass="underline" if test="NAME='Mercury'">

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

   <xsclass="underline" with-param name="COLOR" select="'RED'"/>

  </xsclass="underline" call-template>

 </xsclass="underline" if>

 <xsclass="underline" if test="NAME='Venus'">

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

   <xsclass="underline" with-param name="COLOR" select="'GREEN'"/>

  </xsclass="underline" call-template>

 </xsclass="underline" if>

 <xsclass="underline" if test="NAME='Earth'">

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

  </xsclass="underline" call-template>

 </xsclass="underline" if>

</xsclass="underline" template>

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

 <xsclass="underline" param name="COLOR" select="'blue'"/>

 <TR>

  <TD>

   <FONT COLOR="{$COLOR}"><xsclass="underline" value-of select="NAME"/></FONT>

  </TD>