xsl-list
[Top] [All Lists]

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

2010-05-07 23:38:33
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>
--~--