ソース ドキュメントには、範囲を定義する 2 つの値 (年) が含まれています。それらを変数$year1
とに入れます$year2
。ここで、開始年と終了年を含む、これら 2 つの値の間の年ごとに -Element を出力するために印刷する必要があります。再帰を使用してループを作成する方法はわかりますが、特に. 毎回値を 1 ずつ増やす方法がわかりません。何か案は?
3 に答える
生成する必要があるシーケンスのサイズが事前にわかっておらず、制限されていない場合、ピエズ法は適用できません。
このような場合、XSLT ソリューションは再帰を使用する必要があります。
これは、特定の条件が指定されるまで、最初の入力に対してアクションを実行し、次にその結果に対してアクションを実行する、一般的な「反復」テンプレートです。
この変換は末尾再帰であり、インテリジェントな XSLT プロセッサによりスタック オーバーフローなしで機能します。
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output method="text"/>
<my:action>
<end>1000000</end>
</my:action>
<xsl:variable name="vAction"
select="document('')/*/my:action"/>
<xsl:template match="/">
<xsl:call-template name="iterate">
<xsl:with-param name="pAction" select="$vAction"/>
<xsl:with-param name="pInput" select="0"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="iterate">
<xsl:param name="pAction"/>
<xsl:param name="pInput"/>
<xsl:if test="string-length($pInput)">
<xsl:variable name="vResult">
<xsl:apply-templates select="$pAction">
<xsl:with-param name="pInput" select="$pInput"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:copy-of select="$vResult"/>
<xsl:call-template name="iterate">
<xsl:with-param name="pAction"
select="$pAction"/>
<xsl:with-param name="pInput" select="$vResult"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="my:action">
<xsl:param name="pInput" select="0"/>
<xsl:if test="not($pInput >= end)">
<xsl:value-of select="concat($pInput+1,'
')"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
この変換が任意の XML ドキュメント (使用されていない) に適用されると、反復への末尾再帰を最適化するインテリジェントな XSLT プロセッサが、スタック オーバーフローなしで必要な結果を生成します。これは、結果を生成するために使用した Saxon 6.5.4 の場合です。
問題は、すべての XSLT プロセッサが末尾再帰を認識して最適化するわけではないことです。
このようなプロセッサでは、DVC スタイルの再帰を使用できます。
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="1"/>
<xsl:with-param name="pEnd" select="1000000"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="displayNumbers">
<xsl:param name="pStart"/>
<xsl:param name="pEnd"/>
<xsl:if test="not($pStart > $pEnd)">
<xsl:choose>
<xsl:when test="$pStart = $pEnd">
<xsl:value-of select="$pStart"/>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vMid" select=
"floor(($pStart + $pEnd) div 2)"/>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$pStart"/>
<xsl:with-param name="pEnd" select="$vMid"/>
</xsl:call-template>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$vMid+1"/>
<xsl:with-param name="pEnd" select="$pEnd"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
この変換は、MSXML4 を使用してクラッシュすることなく正しい結果を生成します。
この DVC 変換では、再帰の深さの最大値は Log2(N) (この場合は 19) だけです。
FXSL ライブラリの使用をお勧めします。など、一般的に使用される高階関数の DVC バリアントを提供し、ほぼすべての再帰アルゴリズムの DVC バリアントを生成できるようにしますfoldl()
。map()
もちろん、XSLT2.0 では単純に次のように記述します。
<xsl:sequence select="1 to 1000000"/>
XSLT 1.0 ソリューション
ピエツ法を参照してください。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="year1" select="2010" />
<xsl:variable name="year2" select="2013" />
<xsl:template match="/">
<xsl:for-each select="(//node()|//@*)[position() < ($year2 - $year1 + 2)]">
<xsl:value-of select="concat($year1+position()-1,'
')" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XSLT 2.0 ソリューション
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="year1" select="2010" />
<xsl:variable name="year2" select="2013" />
<xsl:template match="/">
<xsl:value-of select="$year1 to $year2" separator="
" />
</xsl:template>
</xsl:stylesheet>
出力
これらのスタイルシートは両方とも製品を生成します...
2010
2011
2012
2013
助けてくれてありがとう
私は xslt 2.0 で作業しているので、Dimitris の最後の短いメモでうまくいきました。
<xsl:template name="yearranges">
<xsl:param name="year1" />
<xsl:param name="year2" />
<xsl:for-each select="$year1 to $year2">
<year>
<xsl:sequence select="." />
</year>
</xsl:for-each>
</xsl:template>
タグで毎年印刷する必要があります。それは簡単でした。ややこしいのは、call-template で行われる xsl:sequence の場合、年を xs:integer としてフォーマットする必要があることです。
<xsl:call-template name="yearranges">
<xsl:with-param name="year1" as="xs:integer" select="xs:integer($year1)" />
<xsl:with-param name="year2" as="xs:integer" select="xs:integer($year2)" />
</xsl:call-template>
もう一度ありがとう オリバー