I recently wrote an entry to my Dev.AOL blog entitled “Solving FizzBuzz in XSLT 1.0” built upon the premise that in the real world, data changes, and it’s because it changes that instead of thinking of how to solve problems using static data variables, we should instead be trying to solve them in ways that are much more adaptable, and therefore, reusable.
In other words, if your desire is to find someone who truly understands how to write code that solves real-world problems, then use real-world scenarios: Data changes > You’re code shouldn’t have to change with it.
Of course one could argue “code? data? what’s the difference?”, and of course, I would only be able to agree. But none-the-less, there’s still a need to write the initial processing code that will then process and transform the data code into whatever it is it needs to be transformed into, and it’s on this premise I present the following as a picture perfect example of what truly Beautiful Code looks like.
Building upon the theme provided by Dr. Kay a while back, I recently wrote a post that showcased how one could not only use XSLT to solve the problem, but do so in a way that provides for dynamic data evaluation in regards to the variables used, and what those variables should print out when the test evaluates to true.
My question to you all is this: Using this same premise, how would one solve this in XSLT 2.0, and in particular, given that this touches smack-dab center on the benefits that FXSL provides the XSLT developer, how one could use FXSL to solve this same problem?
I been bouncing around different ideas in my head all morning as to the best way to optimize this in XSLT 2.0/FXSL, but as of yet, haven’t come up with something that I think really takes advantage of what XSLT 2.0/FXSL bring to the table. Ideas from the community at large?
Thanks in advance!
“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…”
Within what had to be maybe 15 minutes I received the first solution from David Carlisle, who then followed up again with another solution. Several others then followed. I’ve posted the responses thus far to a new entry entitled “Solving FizzBuzz in XSLT 2.0“, something of which I believe will showcase to anyone who doubts just how truly remarkable and wonderful XSLT 2.0 truly is.
That said, I recently checked my inbox and noticed that one more entry had been posted in response to my inquiry, and this time around it came from none other than Dimitre Novatchev, a colleague in whom I have both the privilege, pleasure, and honor of calling my friend. For most of you he needs no formal introduction, but for those of you unaware, Dimitre is the creator of FXSL (Functional eXtensible Stylesheet Language), and it is with FXSL at its foundation I present to you what I am now officially tagging,
FizzBuzz 2.0 :: An Adventure in Beautiful Code
I have been quite busy these days, so here's an answer coming somewhat late.
The other reason for the delay is that the FXSL infrastructure was not
complete for solving a problem of this kind. I had to implement a
number of new functions (especially the f:lift<N>() family of
functions), which I had planned to do long ago.
Right now, FXSL produces the desired result with the following
one-liner (err… it is a single expression, to be more precise):
"f:multiMap($vDelimElem, (f:compose(f:choice('fizz', '', 0), f:flip(f:mod(),3)), f:compose(f:choice('buzz', '', 0), f:flip(f:mod(),5)), f:lift4(f:choice(), f:const(''), f:id(), f:const((0,3,5,6,9,10,12)), f:flip(f:mod(),15) ) ), 1 to 100)"
Here f:multiMap() maps a list of functions on every element of a list
(of data items). The produced list of results are delimited by an
element-delimiter, because the XPath 2.0 data model only supports flat
f:choice(pChoice1, pChoice2, pChoice1Val, pActualVal)
pActualVal = pChoice1Val
and evaluates to
f:id(x) = x f:const(x,y) = x
So, the above expression evaluates a multiMap of three functions, each
on the numbers 1 to 100.
The first two functions produce "fizz" or (respectively) "buzz"
whenever their argument is divisible by 3 or (respectively) 5. Both
functions produce Nothing (the empty string) otherwise.
The third function specified for f:multiMap() is a little bit more
involved. It is a "lift" of the function f:choice(), which allows its
arguments to be functions, that provide the arguments to f:choice().
This is necessary, because when the number is neither divisible by 3
or 5 we are required to produce not a fixed result, but this number
itself, which is done by the function f:id().
Let me thank M.David Peterson for this excellent and challenging problem.
Here is the complete transformation. To run it one needs to sync from
the FXSL CVS for the latest changes:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; xmlns:xs="http://www.w3.org/2001/XMLSchema"; xmlns:f="http://fxsl.sf.net/"; exclude-result-prefixes="f xs" > <xsl:import href="../f/func-multiMap.xsl"/> <xsl:import href="../f/func-Operators.xsl"/> <xsl:import href="../f/func-id.xsl"/> <xsl:import href="../f/func-const.xsl"/> <xsl:import href="../f/func-choice.xsl"/> <xsl:import href="../f/func-lift.xsl"/> <xsl:import href="../f/func-compose.xsl"/> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:template match="/"> <xsl:variable name="vDelimElem" as="element()"> <map/> </xsl:variable> <multiMap> <xsl:sequence select= "f:multiMap($vDelimElem, (f:compose(f:choice('fizz', '', 0), f:flip(f:mod(),3)), f:compose(f:choice('buzz', '', 0), f:flip(f:mod(),5)), f:lift4(f:choice(), f:const(''), f:id(), f:const((0,3,5,6,9,10,12)), f:flip(f:mod(),15) ) ), 1 to 100)" /> </multiMap> </xsl:template> </xsl:stylesheet>
And the result:
<multiMap> <map/>1 <map/>2 <map/>fizz <map/>4 <map/>buzz <map/>fizz <map/>7 <map/>8 <map/>fizz <map/>buzz <map/>11 <map/>fizz <map/>13 <map/>14 <map/>fizz buzz <map/>16 <map/>17 <map/>fizz <map/>19 <map/>buzz <map/>fizz <map/>22 <map/>23 <map/>fizz <map/>buzz <map/>26 <map/>fizz <map/>28 <map/>29 <map/>fizz buzz <map/>31 <map/>32 <map/>fizz <map/>34 <map/>buzz <map/>fizz <map/>37 <map/>38 <map/>fizz <map/>buzz <map/>41 <map/>fizz <map/>43 <map/>44 <map/>fizz buzz <map/>46 <map/>47 <map/>fizz <map/>49 <map/>buzz <map/>fizz <map/>52 <map/>53 <map/>fizz <map/>buzz <map/>56 <map/>fizz <map/>58 <map/>59 <map/>fizz buzz <map/>61 <map/>62 <map/>fizz <map/>64 <map/>buzz <map/>fizz <map/>67 <map/>68 <map/>fizz <map/>buzz <map/>71 <map/>fizz <map/>73 <map/>74 <map/>fizz buzz <map/>76 <map/>77 <map/>fizz <map/>79 <map/>buzz <map/>fizz <map/>82 <map/>83 <map/>fizz <map/>buzz <map/>86 <map/>fizz <map/>88 <map/>89 <map/>fizz buzz <map/>91 <map/>92 <map/>fizz <map/>94 <map/>buzz <map/>fizz <map/>97 <map/>98 <map/>fizz <map/>buzz <map/> </multiMap>
Truly great madness cannot be achieved without significant intelligence.
To invent, you need a good imagination and a pile of junk
You've achieved success in your field when you don't know whether what
you're doing is work or play