On Fri, May 7, 2010 at 3:34 PM, Costello, Roger L.
<costello(_at_)mitre(_dot_)org> 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>
<StartBalance>100.00</StartBalance>
<Transaction>-5.00</Transaction>
<Transaction>-2.50</Transaction>
<Transaction>10.00</Transaction>
<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?revision=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? /Roger
----------------------------------------------
IMPLEMENTATION #1: Uses Recursive Function
----------------------------------------------
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ex="http://www.example.org"
version="2.0">
<xsl:output method="text" encoding="US-ASCII" />
<xsl:function name="ex:Balance">
<xsl:param name="starting-balance" />
<xsl:param name="transactions" />
<xsl:choose>
<xsl:when test="empty($transactions)" />
<xsl:otherwise>
<xsl:sequence select="$starting-balance + $transactions[1]" />
<xsl:sequence select="ex:Balance($starting-balance +
$transactions[1], $transactions[position() gt 1])" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="BankTransactions">
<xsl:sequence select="ex:Balance(StartBalance, (Transaction))" />
</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"
version="2.0">
<xsl:output method="text" encoding="US-ASCII" />
<xsl:template match="BankTransactions">
<xsl:apply-templates select="Transaction[1]">
<xsl:with-param name="starting-balance"
select="data(StartBalance)" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Transaction">
<xsl:param name="starting-balance" />
<xsl:sequence select="$starting-balance + data(.)" />
<xsl:if test="following-sibling::Transaction">
<xsl:apply-templates select="following-sibling::Transaction[1]">
<xsl:with-param name="starting-balance"
select="$starting-balance + data(.)" />
</xsl:apply-templates>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail:
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--