3

XSLT 2.0で末尾再帰関数を作成しようとしています。この関数は、日付の複数値変数を反復処理し、最も古い変数を返します。何らかの理由で、私の関数はSaxonHE9.4によって末尾再帰として認識されず、入力ファイルに150〜200を超えるエントリがある場合、次のエラーが発生します。

tail_rec_test.xslの73行目のエラー:ネストされた関数呼び出しが多すぎます。無限再帰が原因である可能性があります。組み込みのテンプレートルール

これが私のxml入力です:

<?xml version="1.0"?>
<Events>
  <Event>
    <Date>2004-01-01</Date>
  </Event>
  <Event>
    <Date>2003-01-01</Date>
  </Event>
  <Event>
    <Date>2002-01-01</Date>
  </Event>
  <Event>
    <Date>2001-01-01</Date>
  </Event>
  <Event>
    <Date>2005-01-01</Date>
  </Event>
  <Event>
    <Date>2006-01-01</Date>
  </Event>
  <Event>
    <Date>2007-01-01</Date>
  </Event>
  <Event>
    <Date>2008-01-01</Date>
  </Event>
</Events>

これは私のxslファイルがどのように見えるかです:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:own="http://ownfunctions">
    <xsl:output method="xml" indent="yes"/> 
    <xsl:strip-space elements="*"/>


    <xsl:function name="own:findEarliestDate">
        <xsl:param name="dates" as="xs:date*"/>

        <xsl:variable name="size"><xsl:value-of select="count($dates)" /></xsl:variable>
        <xsl:choose>
            <xsl:when test="$size &gt; 0">
                <xsl:value-of select="own:findEarliestDate-helper($dates, $size, xs:date('2050-01-01'))" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="''"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>


    <xsl:function name="own:findEarliestDate-helper" as="xs:date">
        <xsl:param name="items" as="xs:date*"/>
        <xsl:param name="i" as="xs:integer"/>
        <xsl:param name="curMin" as="xs:date"/>

        <xsl:choose>
            <xsl:when
                test="$i = 0">
                <xsl:value-of select="xs:date($curMin)"/>           
            </xsl:when>
            <xsl:otherwise>
                <xsl:variable name="item" as="xs:date">
                    <xsl:value-of select="xs:date($items[$i])"/>
                </xsl:variable>

                <xsl:variable name="next" as="xs:date">
                    <xsl:choose>
                        <xsl:when test="$item &lt; $curMin">
                            <xsl:value-of select="$item"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select="$curMin"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:variable>

                <xsl:value-of select="own:findEarliestDate-helper($items, $i - 1, $next)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>


    <xsl:template match="Events">
        <xsl:variable name="items" as="xs:date*">
            <xsl:for-each select="Event">
                <xsl:value-of select="xs:date(Date)"/>
            </xsl:for-each>
        </xsl:variable>

        <Test>
            <EarliestDate>
                <xsl:value-of select="own:findEarliestDate($items)"/>
            </EarliestDate>
        </Test>
    </xsl:template>

</xsl:stylesheet>

どうすればそれを正しい末尾再帰関数に変換できますか?この例をテストしましたが、自分のコードに適用することはできません:http: //www.nesterovsky-bros.com/weblog/2008/02/20/EfficientXslt20RecursionInSaxon9.aspx

4

1 に答える 1

5

これを再現することはできません

Saxon 9.4.06EE(評価コピー)を使用すると、結果は次のようになります。

<Test xmlns:fo="http://www.w3.org/1999/XSL/Format"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:fn="http://www.w3.org/2005/xpath-functions"
      xmlns:own="http://ownfunctions">
   <EarliestDate>2001-01-01</EarliestDate>
</Test>

Saxon-EE 9.4.0.6J from Saxonica
Java version 1.6.0_31
Using license serial number XXXXXXXX
Generating byte code...
Stylesheet compilation time: 2168 milliseconds
Processing file:/C:/Program%20Files/Java/jre6/bin/marrowtr.xml
Using parser com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser
Building tree for file:/C:/Program%20Files/Java/jre6/bin/marrowtr.xml using class net.sf.saxon.tree.tiny.TinyBuilder
Tree built in 10 milliseconds
Tree size: 27 nodes, 80 characters, 0 attributes
Execution time: 122ms
Memory used: 52169472
NamePool contents: 8 entries in 8 chains. 6 URIs

ところで、これだけを使用して同じ結果を生成できます

<xsl:value-of select="min(/*/*/Date/xs:date(.))"/>

更新

問題は次の行にあります。

            <xsl:value-of select="own:findEarliestDate-helper($items, $i - 1, $next)"/>

関数の戻り型はであるためxs:date、上記の行は関数の実行シーケンスの最後の行ではありません。文字列(より正確にはテキストノード)を生成し、XSLTプロセッサはこの文字列を取得してxs:date-に変換する必要があります。つまり、関数によって占有されているメモリは破棄されず、コールスタックはそれがオーバーフローするまで、成長します。

解決策は簡単です:

上記を次のように置き換えます。

            <xsl:sequence select="own:findEarliestDate-helper($items, $i - 1, $next)"/>

これにより、が生成xs:dateされ、XSLTプロセッサは関数を末尾再帰として認識します。

修正されたコードを1000イベント(元のコードがクラッシュする)でテストしたところ、結果は正常に(そしてより速く)生成されました。

Saxon-EE 9.4.0.6J from Saxonica
Java version 1.6.0_31
Using license serial number XXXXXXXXXX
Generating byte code...
Stylesheet compilation time: 2002 milliseconds
Processing file:/C:/Program%20Files/Java/jre6/bin/marrowtr.xml
Using parser com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser
Building tree for file:/C:/Program%20Files/Java/jre6/bin/marrowtr.xml using class net.sf.saxon.tree.tiny.TinyBuilder
Tree built in 124 milliseconds
Tree size: 3032 nodes, 9800 characters, 0 attributes
Execution time: 364ms
Memory used: 55089048
NamePool contents: 8 entries in 8 chains. 6 URIs
于 2012-12-16T03:40:56.547 に答える