Subject: Re: RE: Is xsl:for-each "syntactic sugar"?
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Fri, 7 May 2010 21:37:47 -0700
|
On Fri, May 7, 2010 at 3:34 PM, Costello, Roger L. <costello@xxxxxxxxx>
wrote:
> Hi Folks,
>
> Suppose that I want to write an XSLT transform that outputs a bank account
balance after each debit/credit transaction. Here's an XML document that has
the start balance followed by each transaction:
>
> <?xml version="1.0"?>
> <BankTransactions>
> B B <StartBalance>100.00</StartBalance>
> B B <Transaction>-5.00</Transaction>
> B B <Transaction>-2.50</Transaction>
> B B <Transaction>10.00</Transaction>
> B B <Transaction>-7.50</Transaction>
> </BankTransactions>
>
> The output should be:
>
> 95 92.5 102.5 95
>
> I do not believe that this task can be accomplished using xsl:for-each. Do
you agree?
Here is a solution using the f:scanl() function of FXSL. The
complexity of the implemented algorithm is O(N) and I don't believe
that this linear algorithm can be expressed with <xsl:for-each>.
The function f:scanl() is very similar to f:foldl(), but at each
application of the parameter-function, the intermediate result is also
output.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs f"
>
<xsl:import href="../f/func-scanlDVC.xsl"/>
<xsl:import href="../f/func-Operators.xsl"/>
<!-- To be applied on testFunc-scanlDVC4.xml -->
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:sequence select="f:scanl(f:add(), 0, *)"/>
</xsl:template>
</xsl:stylesheet>
Result:
0 100 95 92.5 102.5 95
As can be seen from the code of the function in the CVS of FXSL (at:
http://fxsl.cvs.sourceforge.net/viewvc/fxsl/fxsl-xslt2/f/func-scanl.xsl?revis
ion=1.1&view=markup)
the function is recursive:
1 <xsl:stylesheet version="2.0"
2 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
3 xmlns:f="http://fxsl.sf.net/"
4 exclude-result-prefixes="f"
5 >
6 <xsl:import href="func-apply.xsl"/>
7
8 <xsl:function name="f:scanl">
9 <xsl:param name="pFunc" as="element()"/>
10 <xsl:param name="pA0"/>
11 <xsl:param name="pList" as="item()*"/>
12
13 <xsl:sequence select=
14 "$pA0,
15 (if (exists($pList))
16 then
17 f:scanl($pFunc,
18 f:apply($pFunc, $pA0, $pList[1]),
19 $pList[position() > 1]
20 )
21 else ()
22 )"
23 />
24 </xsl:function>
25
26 <xsl:function name="f:scanl1">
27 <xsl:param name="pFun" as="element()"/>
28 <xsl:param name="pList" as="item()+"/>
29
30 <xsl:sequence select="f:scanl($pFun, $pList[1],
$pList[position() > 1])"/>
31 </xsl:function>
32 </xsl:stylesheet>
A DVC-recursive variant is also in the library, but its code is twice
as long and not so easily readable.
--
Cheers,
Dimitre Novatchev
---------------------------------------
Truly great madness cannot be achieved without significant intelligence.
---------------------------------------
To invent, you need a good imagination and a pile of junk
-------------------------------------
Never fight an inanimate object
-------------------------------------
You've achieved success in your field when you don't know whether what
you're doing is work or play
>
> Below are two implementations. The first implementation uses a recursive
function. The second uses xsl:apply-templates to recursively fire a template.
Which implementation is better? Why? Is there a better implementation than the
two shown below? B /Roger
>
> ----------------------------------------------
> IMPLEMENTATION #1: Uses Recursive Function
> ----------------------------------------------
> <?xml version="1.0" encoding="US-ASCII"?>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> B B B B B B B B xmlns:ex="http://www.example.org"
> B B B B B B B B version="2.0">
>
> B B <xsl:output method="text" encoding="US-ASCII" />
>
>
> B B <xsl:function name="ex:Balance">
> B B B B <xsl:param name="starting-balance" />
> B B B B <xsl:param name="transactions" />
>
> B B B B <xsl:choose>
> B B B B B B <xsl:when test="empty($transactions)" />
> B B B B B B <xsl:otherwise>
> B B B B B B B B <xsl:sequence select="$starting-balance +
$transactions[1]" />
> B B B B B B B B <xsl:sequence select="ex:Balance($starting-balance +
$transactions[1], $transactions[position() gt 1])" />
> B B B B B B </xsl:otherwise>
> B B B B </xsl:choose>
> B B </xsl:function>
>
> B B <xsl:template match="BankTransactions">
> B B B B <xsl:sequence select="ex:Balance(StartBalance, (Transaction))"
/>
> B B </xsl:template>
>
> </xsl:stylesheet>
>
> --------------------------------------------------------------------------
> IMPLEMENTATION #2: Uses xsl:apply-templates to Recursively Fire a Template
> --------------------------------------------------------------------------
> <?xml version="1.0" encoding="US-ASCII"?>
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> B B B B B B B B version="2.0">
>
> B B <xsl:output method="text" encoding="US-ASCII" />
>
>
> B B <xsl:template match="BankTransactions">
> B B B B <xsl:apply-templates select="Transaction[1]">
> B B B B B B <xsl:with-param name="starting-balance"
select="data(StartBalance)" />
> B B B B </xsl:apply-templates>
> B B </xsl:template>
>
>
> B B <xsl:template match="Transaction">
> B B B B <xsl:param name="starting-balance" />
>
> B B B B <xsl:sequence select="$starting-balance + data(.)" />
>
> B B B B <xsl:if test="following-sibling::Transaction">
> B B B B B B <xsl:apply-templates
select="following-sibling::Transaction[1]">
> B B B B B B B B <xsl:with-param name="starting-balance"
select="$starting-balance + data(.)" />
> B B B B B B </xsl:apply-templates>
> B B B B </xsl:if>
> B B </xsl:template>
>
> </xsl:stylesheet>
|