【发布时间】:2019-10-15 01:05:38
【问题描述】:
谁能解释为什么下面的代码会出现堆栈溢出?我曾希望 Saxon 将模板识别为尾递归,并对其进行优化,允许大量迭代 - 实际上它在大约 1000 次迭代后会出现堆栈溢出。我正在执行如下:
me@server:~/dev$ java -classpath /usr/local/share/java/saxon9ee.jar net.sf.saxon.Transform -it -xsl:recurse.xslt
437
Exception in thread "main" java.lang.StackOverflowError
at net.sf.saxon.expr.instruct.ParameterSet.getIndex(ParameterSet.java:127)
at net.sf.saxon.expr.XPathContextMajor.useLocalParameter(XPathContextMajor.java:561)
at EE_sequence_02125238280.process(file:/home/me/dev/recurse.xslt:23)
at com.saxonica.ee.bytecode.CompiledExpression.process(CompiledExpression.java:84)
at com.saxonica.ee.bytecode.ByteCodeCandidate.process(ByteCodeCandidate.java:143)
at com.saxonica.ee.bytecode.ByteCodeCandidate.processLeavingTail(ByteCodeCandidate.java:178)
at net.sf.saxon.expr.instruct.NamedTemplate.expand(NamedTemplate.java:263)
at EE_sequence_02125238280.process(file:/home/me/dev/recurse.xslt:23)
and so on.....
我正在使用 Saxon-EE 9.8.0.15J。
我尝试过使用<xsl:if>、XPATH 和函数来代替<xsl:choose>,但我遇到了同样的问题。
使用call-templates,我实际上可以在网上找到 cmets 建议这应该可以工作,下面的示例与我的类似。我不是 100% 清楚是否支持 XPATH 表达式中的函数或递归调用,因此我在此示例中坚持使用 call-templates。
例如:Recursive Loop XSLT
我想我错过了一个技巧 - 有什么想法吗?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:map="http://www.w3.org/2005/xpath-functions/map" exclude-result-prefixes="xs map">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template name="xsl:initial-template">
<xsl:variable name="freqs" select="unparsed-text-lines('input.txt', 'UTF-8')!xs:integer(.)"/>
<xsl:message select="sum($freqs)"/>
<xsl:variable name="hash" select="map{}" as="map(xs:integer, xs:boolean)"/>
<xsl:call-template name="find-repeated-cs">
<xsl:with-param name="freqs" select="$freqs"/>
<xsl:with-param name="cs-hash" select="$hash"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="find-repeated-cs">
<xsl:param name="freqs" as="xs:integer*"/>
<xsl:param name="cs-hash" as="map(xs:integer, xs:boolean)"/>
<xsl:param name="cs" select="0" as="xs:integer"/>
<xsl:param name="i" select="1" as="xs:integer"/>
<xsl:variable name="new-cs" select="$cs + $freqs[$i]" as="xs:integer"/>
<xsl:variable name="new-i" select="if ($i >= count($freqs)) then 1 else $i + 1" as="xs:integer"/>
<xsl:choose>
<xsl:when test="map:contains($cs-hash, $new-cs)">
<xsl:value-of select="$new-cs"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="find-repeated-cs">
<xsl:with-param name="freqs" select="$freqs"/>
<xsl:with-param name="cs-hash" select="map:put($cs-hash,$new-cs,true())"/>
<xsl:with-param name="cs" select="$new-cs"/>
<xsl:with-param name="i" select="$new-i"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
编辑
对于一些上下文,代码在累积和序列中找到第二次出现的数字,该数字是通过重复循环一组固定的整数 freqs 而生成的。最新的累积和是cs,过去看到的累积和的字典在cs-hash 中建立。 i 将 freq 索引为循环索引/计数器。
如果我的方法很愚蠢,我也对其他方法感兴趣,但我仍然想了解为什么无法优化此代码 - 即使有更好的方法。
编辑 2
为了完整起见,使用xsl:choose:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:aoc2018="\
http://www.blah.co.uk/aoc2018" exclude-result-prefixes="xs map aoc2018">
<!-- hint: java -classpath /usr/local/share/java/saxon9ee.jar net.sf.saxon.Transform -it -xsl:01.xslt -->
<xsl:function name="aoc2018:find-repeated-cs">
<xsl:param name="freqs" as="xs:integer*"/>
<xsl:param name="cs-hash" as="map(xs:integer, xs:boolean)"/>
<xsl:param name="cs" as="xs:integer"/>
<xsl:param name="i" as="xs:integer"/>
<xsl:variable name="new-cs" select="$cs + $freqs[$i]" as="xs:integer"/>
<xsl:choose>
<xsl:when test="map:contains($cs-hash, $new-cs)">
<xsl:value-of select="$new-cs"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="new-i" select="if ($i >= count($freqs))
then 1
else $i + 1" as="xs:integer"/>
<xsl:value-of select="aoc2018:find-repeated-cs($freqs, map:put($cs-hash,$new-cs,true()), $new-cs, $new-i)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template name="xsl:initial-template">
<xsl:variable name="freqs" select="unparsed-text-lines('input.txt', 'UTF-8')!xs:integer(.)"/>
<xsl:message select="sum($freqs)"/>
<xsl:variable name="hash" select="map{}" as="map(xs:integer, xs:boolean)"/>
<xsl:message select="aoc2018:find-repeated-cs($freqs, $hash, 0, 1)"/>
</xsl:template>
</xsl:stylesheet>
以及使用 XPATH 的函数实现:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:aoc2018="\
http://www.blah.co.uk/aoc2018" exclude-result-prefixes="xs map aoc2018">
<!-- hint: java -classpath /usr/local/share/java/saxon9ee.jar net.sf.saxon.Transform -it -xsl:01.xslt -->
<xsl:function name="aoc2018:find-repeated-cs">
<xsl:param name="freqs" as="xs:integer*"/>
<xsl:param name="cs-hash" as="map(xs:integer, xs:boolean)"/>
<xsl:param name="cs" as="xs:integer"/>
<xsl:param name="i" as="xs:integer"/>
<xsl:variable name="new-cs" select="$cs + $freqs[$i]" as="xs:integer"/>
<xsl:variable name="new-i" select="if ($i >= count($freqs))
then 1
else $i + 1" as="xs:integer"/>
<xsl:value-of select="if (map:contains($cs-hash, $new-cs))
then $new-cs
else aoc2018:find-repeated-cs($freqs, map:put($cs-hash,$new-cs,true()), $new-cs, $new-i)"/>
</xsl:function>
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template name="xsl:initial-template">
<xsl:variable name="freqs" select="unparsed-text-lines('input.txt', 'UTF-8')!xs:integer(.)"/>
<xsl:message select="sum($freqs)"/>
<xsl:variable name="hash" select="map{}" as="map(xs:integer, xs:boolean)"/>
<xsl:message select="aoc2018:find-repeated-cs($freqs, $hash, 0, 1)"/>
</xsl:template>
</xsl:stylesheet>
【问题讨论】:
-
当您使用 XSLT 3 时,我想说避免 StackOverflowError 并确保“尾调用优化”的正确尝试是使用
xsl:iterate。我也不太确定你想要实现什么,无论是左折叠还是一些相邻的分组都不是一种更简单的方法。如果您解释代码应该执行哪种计算,将会有所帮助。 -
@MartinHonnen - 谢谢,看看
xsl:iterate- 我在底部的原始问题中添加了一些细节,描述了应该发生的事情。代码正确生成累积总和,直到引发堆栈溢出。 -
但是对于任何输入集,递归是否应该停止?你不是一次又一次地总结价值,即使是像
1,2,3这样的简单序列? -
@MartinHonnen - 它不会为每个输入集停止,但会为某些输入集。这是一个古老的代码出现问题 - 示例输入集可在此处获得 adventofcode.com/2018/day/1/input - 我的各种语言的工作实现可在此处查看 github.com/falloutphil/aoc_2018/tree/master/day_01 - Python 最容易理解,R 最接近上面的 XSLT。 XSLT 实现与工作实现的输出相匹配(直到堆栈溢出)。我知道 XSLT 不是完美的语言,我很享受挑战!
-
github.com/falloutphil/aoc_2018/blob/master/day_01/input.txt 文件似乎可以在 9.8 HE、9.9 HE 和 9.9 EE 中正常工作,所以我想在 9.8 EE 中打开字节码编译似乎是个问题。让我们等待 Saxonica/Michael Kay 是否看到该线程并认为它是一个需要修复的错误。
标签: recursion xslt xpath saxon xslt-3.0