Subject: Re: [xsl] Sequential numbers in pure xslt, breaking the no-side-effect rule From: "Dimitre Novatchev" <dnovatchev@xxxxxxxxx> Date: Fri, 16 Mar 2007 10:16:29 -0700 |
Here is an example how to write any XSLT transformation (the function f:transform) that will have the ability to get the next "tick" (next consecutive sequence number) at any time.
The transformation will only need (this can be done almost entirely in a wrapper) to surround its oun output with two "coventional outputs" -- the new tick count and the stop condition for the transformation.
f:iterUntil() (yes, that same FXSL function) iterates whatever function is passed as its second argument until the stop condition (the predicate passed as its first argument) evaluates to true().
This is a very generic approach. *Any* transformation can be substituted for the body of f:transform().
The only discipline is the layout of the results and to start a new iteration (for example whenever it thinks something significant has happened that needs to increment the tick counter) by returning with a false() stop condition.
<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">
<!-- To be applied on any xml file, or with initial template named "initial" -->
<xsl:variable name="vExecutionLimit" as="xs:integer" select="100"/> <xsl:variable name="vNL" select="'
'"/>
<xsl:variable name="vRawResults" select= "f:iterUntil(f:last(), f:transform(), (0,false()))"/>
Last tick: <xsl:sequence select="$vRawResults[1]"/> Stop condition: <xsl:sequence select="$vRawResults[last()]"/>
Transform output: <xsl:text/> <xsl:sequence select= "for $i in 2 to count($vRawResults) -1 return $vRawResults[$i]" />
<xsl:function name="f:transform" as="item()+"> <xsl:param name="pWorld" as="item()+"/>
<xsl:variable name="vmyResults" as="item()*"> <!-- Do whatever processing is needed here --> <!-- and produce our "useful" results -->
<xsl:sequence select= " remove(remove($pWorld, count($pWorld)),1), $vnewTick, ' ', 3*$vnewTick, $vNL"/>
<!-- Set the stop condition in the last item --> <xsl:sequence select= "if($vnewTick gt $vExecutionLimit) then true() else false() "/> </xsl:variable>
<!-- Produce the results --> <!-- The "tick" is always the 1st item returned -->
<xsl:sequence select="$vnewTick, $vmyResults"/> </xsl:function>
<xsl:function name="f:transform" as="element()"> <f:transform/> </xsl:function>
<xsl:template match="f:transform" as="item()+" mode="f:FXSL"> <xsl:param name="arg1" as="item()+"/>
<xsl:sequence select="f:transform($arg1)"/> </xsl:template>
<xsl:function name="f:last" as="xs:boolean"> <xsl:param name="arg1" as="item()*"/>
<xsl:sequence select="xs:boolean($arg1[last()])"/> </xsl:function>
<xsl:function name="f:last" as="element()"> <f:last/> </xsl:function>
<xsl:template match="f:last" as="xs:boolean" mode="f:FXSL"> <xsl:param name="arg1" as="item()*"/>
<xsl:sequence select="f:last($arg1)"/> </xsl:template> </xsl:stylesheet>
In this case I show a transformation, which can use the tick count at any point. To illustrate doing some useful work, it produces the tick count multiplied by 3.
Last tick: 101 Stop condition: true
Transform output: 1 3 2 6 3 9 4 12 5 15 6 18 7 21 8 24 9 27 10 30 11 33 12 36 13 39 14 42 15 45 16 48 17 51 18 54 19 57 20 60 21 63 22 66 23 69 24 72 25 75 26 78 27 81 28 84 29 87 30 90 31 93 32 96 33 99 34 102 35 105 36 108 37 111 38 114 39 117 40 120 41 123 42 126 43 129 44 132 45 135 46 138 47 141 48 144 49 147 50 150 51 153 52 156 53 159 54 162 55 165 56 168 57 171 58 174 59 177 60 180 61 183 62 186 63 189 64 192 65 195 66 198 67 201 68 204 69 207 70 210 71 213 72 216 73 219 74 222 75 225 76 228 77 231 78 234 79 237 80 240 81 243 82 246 83 249 84 252 85 255 86 258 87 261 88 264 89 267 90 270 91 273 92 276 93 279 94 282 95 285 96 288 97 291 98 294 99 297 100 300 101 303
This is trivial (and actually quite boring) to achieve in a functional language. To quote Simon Peyton-Jones, "Haskell is the finest imperative language" :o)
-- Cheers, Dimitre Novatchev --------------------------------------- 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
Hi XSLT'ers,
AFAIK the answer to this is a simple, short "no, cannot be done". But I wanted to check it with the specialists to be sure ;)
The request is simple: create a function that returns '1' on first call, and returns one higher each next call, without overlaps or jumps, without any input. Let's call it my:seq-number:
<xsl:function name="my:seq-number" as="xs:integer"> <!-- some implementation --> </xsl:function>
then the following line would return '1 2 3': <xsl:value-of select="my:seq-number(), my:seq-number(), my:seq-number()" />
There are three scenarios to resolve this issue:
1. Use an extension function in the host language (java, .net) 2. Use an extension instruction like saxon:assign 3. Use 'hidden features' of generate-id().
The first two are obvious. The last one is the best I good get with "pure xslt", but as I tested, does not work with AltovaXML (in fact, it is not even guaranteed to work on other or future versions of Saxon):
<xsl:function name="my:seq:number" as="xs:integer"> <xsl:variable name="node"><xsl:comment /></xsl:variable> <xsl:sequence select="xs:integer(substring(generate-id($node), 2) - 1" /> </xsl:function>
this will indeed return 1,2,3 etc, provided that, depending on the order of execution, the first node that is auto-created is this node, and that no other nodes are created meanwhile. (FYI, Saxon numbers document nodes as d1, d2 etc, where 'd1' is the input doc).
Doing this with AltovaXML generates too much randomness, i.e., the output of generate-id is: idroot219951944 idroot219959376 etc (this is not a non-comformance bug. Not being able to deal with empty <xsl:comment /> is, however, but I am getting adrift...).
Am I overlooking something, or is this about the best I can get? I know I am in violation and that requesting for functions/variables/templates that return different results with the same input is asking for breaking the rules.
Thanks for any extra insights on an already overly discussed subject,
Cheers, -- Abel Braaksma
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] Sequential numbers in pur, David Carlisle | Thread | Re: [xsl] Sequential numbers in pur, Abel Braaksma |
Re: [xsl] Sequential numbers in pur, Abel Braaksma | Date | [xsl] Variation on adjaceny list qu, Simon Shutter |
Month |