2012-12-16 24 views
3

我想在XSLT 2.0中編寫一個尾遞歸函數,該函數遍歷一個多值變量的日期並返回最早的一個。出於某種原因,我的功能不受SaxonHE9.4認定爲尾遞歸,我得到以下錯誤,當輸入文件已經超過150-200項左右:在tail_rec_test線73XSLT 2.0中的尾遞歸函數不起作用

錯誤。 xsl:調用太多嵌套函數 。可能是由於無限遞歸。在內置模板規則

這裏是我的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

回答

5

我不能瑞普這個

使用撒克遜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 

BTW,一個可以只使用它來產生相同的結果

<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 
+0

感謝您的回覆速度快(+1)!我測試了短版本,它絕對有效(5000個事件很有效)。雖然很高興知道爲什麼我的函數不像尾遞歸函數那樣工作。它在150-200個事件中搜索,但這可能取決於VM堆大小。你可以請嘗試很多事件嗎?我可以更新輸入,但它會很大。只要說你喜歡什麼。再次感謝。 – Ivo

+0

@Ivo,我發現你的問題,並用問題,解釋和簡單的解決方案更新了我的答案。 –

+0

太棒了!它現在完美運行,謝謝! – Ivo