Question

I'm having trouble understanding why XSLT (Saxon 9.1 and 9.5) is working the way it does when using a variable containing a sorted sequence.

Here is the output of my program with my three questions embedded:

<?xml version="1.0" encoding="UTF-8"?>
The following sequence is unsorted...

  sequence $list                        = (<contribution e="4"/><contribution e="1"/><contribution e="2"/><contribution e="8"/>)

It is, as I expected, in document order.

The following sequence is explicitly sorted...

  sequence $sorted-list                 = (<contribution e="8"/><contribution e="4"/><contribution e="2"/><contribution e="1"/>)

It is, as I expected, sorted in descending numerical @e order.

In the following output, I expect for the @e values from $list to be in document order,
and for the @e values from $sorted-list to be in descending numerical order...

  value-of $list/@e                     = (4 1 2 8)
  value-of $sorted-list/@e              = (4 1 2 8)

But both are in document order.

Specifically, the $sorted-list/@e values are NOT listed in descending numerical @e order.
(Question 1: ...By the way, why can't I use 'xsl:sequence select="$list/@e"' here?)

Next, here's the real work that I'm interested in. It is a function that computes a running
subtotal of the elements passed in. Order is critical here. I expect for my sorted list to provide
@e values to the function in the order that I explicitly put them in the definition of $sorted-list.
So, in the following call, I send in the sequence that I think SHOULD have been sorted...

  sequence f($sorted-list/@e)           = (0 4 5 7 15)

But my result set is a list of running subtotals that are obviously not correctly ordered.
Question 2: Is this a bug, or evidence of a gap in my comprehension?

In the following call, I send a sequence that's explicitly ordered...

  sequence f(for...$sorted-list[$i]/@e) = (0 8 12 14 15)

...and I receive the sequence I need. I can use this as a workaround.

Question 3: Why is it that I must explicitly [re-]sort the $sorted-list sequence as I pass it to util:f()?

Here's my input XML:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <contribution e="4"/>
    <contribution e="1"/>
    <contribution e="2"/>
    <contribution e="8"/>
</root>

...And my XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:util="http://www.method-r.com/util"
    exclude-result-prefixes="xs util"
    version="2.0">

    <xsl:template match="/">
The following sequence is unsorted...

  sequence $list                        = (<xsl:sequence select="$list"/>)

It is, as I expected, in document order.

The following sequence is explicitly sorted...

  sequence $sorted-list                 = (<xsl:sequence select="$sorted-list"/>)

It is, as I expected, sorted in descending numerical @e order.

In the following output, I expect for the @e values from $list to be in document order,
and for the @e values from $sorted-list to be in descending numerical order...

  value-of $list/@e                     = (<xsl:value-of select="$list/@e"/>)
  value-of $sorted-list/@e              = (<xsl:value-of select="$sorted-list/@e"/>)

But both are in document order.

Specifically, the $sorted-list/@e values are NOT listed in descending numerical @e order.
(Question 1: ...By the way, why can't I use 'xsl:sequence select="$list/@e"' here?)

Next, here's the real work that I'm interested in. It is a function that computes a running
subtotal of the elements passed in. Order is critical here. I expect for my sorted list to provide
@e values to the function in the order that I explicitly put them in the definition of $sorted-list.
So, in the following call, I send in the sequence that I think SHOULD have been sorted...

  sequence f($sorted-list/@e)           = (<xsl:sequence select="util:f(0, $sorted-list/@e)"/>)

But my result set is a list of running subtotals that are obviously not correctly ordered.
Question 2: Is this a bug, or evidence of a gap in my comprehension?

In the following call, I send a sequence that's explicitly ordered...

  sequence f(for...$sorted-list[$i]/@e) = (<xsl:sequence select="util:f(0, for $i in 1 to count($sorted-list) return $sorted-list[$i]/@e)"/>)

...and I receive the sequence I need. I can use this as a workaround.

Question 3: Why is it that I must explicitly [re-]sort the $sorted-list sequence as I pass it to util:f()?

<xsl:text/>
    </xsl:template>



    <xsl:variable name="list" as="element()*" select="//contribution"/>

    <xsl:variable name="sorted-list" as="element()*">
        <xsl:perform-sort select="$list">
            <xsl:sort select="number(@e)" order="descending"/>
        </xsl:perform-sort>
    </xsl:variable>



    <xsl:function name="util:f" as="xs:double*">
        <xsl:param name="sum0" as="xs:double"/>
        <xsl:param name="list" as="xs:double*"/>
        <xsl:sequence select="util:cumulative-list-2((), $list)"/>
    </xsl:function>

    <xsl:function name="util:cumulative-list-2" as="xs:double*">
        <xsl:param name="list" as="xs:double*"/>
        <xsl:param name="list-remainder" as="xs:double*"/>
        <xsl:choose>
            <xsl:when test="not(exists($list-remainder))">
                <xsl:sequence select="$list"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="util:cumulative-list-2(
                    if (empty($list)) then
                    (0, $list-remainder[1])
                    else 
                    ($list, $list[last()] + $list-remainder[1]),
                    remove($list-remainder, 1)
                    )"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function> 


</xsl:stylesheet>
Était-ce utile?

La solution

With a step doing $sorted-list/@e the /@e will select all @e attributes and sort in document order thus if you have your sorted sequence and want to output the @e attributes in the order of the sequence have you can't use $sorted-list/@e, instead you have to use for $item in $sorted-list return $item/@e.

See http://www.w3.org/TR/xpath20/#id-path-expressions which says:

Each operation E1/E2 is evaluated as follows: ... If every evaluation of E2 returns a (possibly empty) sequence of nodes, these sequences are combined, and duplicate nodes are eliminated based on node identity. The resulting node sequence is returned in document order.

If you use the commercial editions of Saxon 9.5 then you should also be able to make use of http://www.w3.org/TR/xpath-30/#id-map-operator and $sorted-list ! @e to preserve the order.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top