Может кто-нибудь объяснить, почему приведенный ниже код получает переполнение стека? Я надеялся, что 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
я могу фактически найти комментарии онлайн, предлагающие это, с примерами, подобными моим ниже. Я не был на 100% уверен, поддерживаются ли функции или рекурсивный вызов внутри выражения XPATH, поэтому я остановился на call-templates
для этого примера. Например: Рекурсивный цикл 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
indexes 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>