[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] 99 bottles of beer


Subject: Re: [xsl] 99 bottles of beer
From: "M. David Peterson" <m.david@xxxxxxxxxx>
Date: Mon, 05 Feb 2007 14:28:16 -0700

So now the *REAL* challenge exists in what Dr. Kay pointed out in a
follow-up comment to that same thread
[http://blogs.msdn.com/mfussell/archive/2004/05/13/130969.aspx#131098 ],

    > It's a little bit odd to try and prove your point with an example
that makes no use of XML input or XML output,


Which, of course, I completely looked over as well with my original post.


Gentleman? ;-)


On Mon, 05 Feb 2007 06:04:59 -0700, Andrew Welch <andrew.j.welch@xxxxxxxxx> wrote:

On 2/5/07, Abel Braaksma <abel.online@xxxxxxxxx> wrote:
Andrew Welch wrote:
> After seeing M. David's post about the bottles of beer problem, I
> thought about how to solve this problem using XSLT 2.0.  Here's what
> came to mind first:

Hi Andrew,

let me try what came to mind second ;)
Here's my go on it in a single XPath statement. A bit less well-suited
for educational purposes. It shows nested for-loops in XPath, casting,
use of sequences+separator and some ways how not to code (it is exactly
an example of good programming practice ;)

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xsl:output method="text" />
    <xsl:template match="/" name="main">
        <xsl:value-of select="
            for $i in reverse(1 to 99)
            return for $j in
                (' on the wall.',
                '.&#10;Take one down, pass it around',
                ' on the wall.&#10;' )

                return concat
                    ($i - xs:integer(ends-with($j, '&#10;')),
                    ' bottle', if ($i = 1) then '' else 's',
                    ' of beer', $j)
        " separator="&#10;"/>

    </xsl:template>
</xsl:stylesheet>

Nice! However a couple of small problems:


2 bottles of beer on the wall.
2 bottles of beer.
Take one down, pass it around
1 bottles of beer on the wall.
      ^^^^^^^

1 bottle of beer on the wall.
1 bottle of beer.
Take one down, pass it around
0 bottle of beer on the wall.
       ^^^^^^^^

You just need to modify it slightly:

<xsl:value-of select="for $i in reverse(1 to 3), $j in
               (' on the wall.',
               '.&#10;Take one down, pass it around',
               ' on the wall.&#10;' )

               return concat
                   ($i - xs:integer(ends-with($j, '&#10;')),
                   ' bottle', if ($i - xs:integer(ends-with($j,
'&#10;')) = 1) then '' else 's',
                   ' of beer', $j)"

separator="&#10;"/>

which gives:

2 bottles of beer on the wall.
2 bottles of beer.
Take one down, pass it around
1 bottle of beer on the wall.

1 bottle of beer on the wall.
1 bottle of beer.
Take one down, pass it around
0 bottles of beer on the wall.

cheers
andrew





-- /M:D

M. David Peterson
http://mdavid.name | http://www.oreillynet.com/pub/au/2354


Current Thread
Keywords