=> Главная База Знаний Xslt Рекурсивный вызов шаблонов


Рекурсивный вызов шаблонов

Рекурсивный вызов шаблонов

Эта тема предназначена, главным образом, для программистов, поскольку здесь я буду пользоваться XSLT как языком программирования. В частности, я реализую вызов именованным шаблоном самого себя, то есть рекурсивный вызов. Классический пример рекурсии — вычисление факториала: например, факториал 6, что записывается как

6!
, равен
6*5*4*3*2*1
, или
720
.

При реализации рекурсии в настоящем языке программирования создается функция — например,

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. Основная идея состоит в том, что значение, возвращаемое шаблоном, можно сохранять в переменной, если шаблон вызывается внутри элемента

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

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"

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

 <xsl:template match="/">

  <xsl:variable name="result">

   <xsl:call-template name="factorial">

    <xsl:with-param name="value" select="6"/>

   </xsl:call-template>

  </xsl:variable>

  6! = <xsl:value-of select="$result"/>

 </xsl:template>

 .

 .

 .

Следующий пример демонстрирует, как можно реализовать шаблон

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

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"

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

 <xsl:template match="/">

  <xsl:variable name="result">

   <xsl:call-template name="factorial">

    <xsl:with-param name="value" select="6"/>

   </xsl:call-template>

  </xsl:variable>

  6! = <xsl:value-of select="$result"/>

 </xsl:template>


 <xsl:template name="factorial">

  <xsl:param name="value"/>

  <xsl:choose>

   <xsl:when test="$value=1">

    <xsl:value-of select="1"/>

   </xsl:when>

   <xsl:otherwise>

    <xsl:variable name="temp">

     <xsl:call-template name="factorial">

      <xsl:with-param name="value" select="$value - 1"/>

     </xsl:call-template>

    </xsl:variable>

    <xsl:value-of select="$temp * $value"/>

   </xsl:otherwise>

  </xsl:choose>

 </xsl:template>

</xsl:stylesheet>

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

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

6! = 720

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