Re: [xsl] RE: Is xsl:for-each "syntactic sugar"?

Subject: Re: [xsl] 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>

Current Thread