Subject: Re: [xsl] Re: lookup-table thoughts (was Re: matching multiple times, outputting once? From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx> Date: Wed, 7 Nov 2001 09:50:59 +0000 |
Hi Tom, > Still, I'd like to propose a variation on the Kay/Novatchev > solutions. My version is longer, but I think that I think that > coming up with it requires less original thought than either of > theirs, because rather than writing a specific recursion I'm > editting a general version. For instance, I'm going to keep the bold > -> b, italic -> i, underline -> u mapping in a data table, so even > though it requires several lines it requires no thought, 'cos I can > start out a stylesheet like so, just editting the data from my > backwards-solution post or from any similar lookup-table-based > stylesheet: You seem to be quite taken with the idea of using XML to store lookup tables within a stylesheet (and rightly so :) Note that this is going to become even easier with XSLT 2.0 when you will be able to declare a variable holding the table: <xsl:variable name="trans"> <in>bold</in> <out>b</out> <in>italic</in> <out>i</out> <in>underline</in> <out>u</out> </xsl:variable> and then use that variable directly as $trans without having to access it as document('')/*/trans:trans. You can try this out already in processors that implement the XSLT 1.1 WD, such as Saxon and jd.xslt. Also, it's likely that in XPath 2.0 you'll be able to use functions as 'steps' in a location path, so that you will be able to do things like: $trans/key('transkey', $in) in order to get the result of using the key on the node tree generated in the $trans variable. As you can see, this will also make it easier to access the value from the key without using a named template. > Now, I want to override that for "emphasis" elements; I want to do > something to "emphasis" elements that will run through the sequence > of their attributes, using child nodes as base. I'm thinking of a > functional-programming pattern > > accum(f,[],base) = base > accum(f,[hd]+tail,base) = f(hd,accum(f,tail,base)); > > (the sum of a sequence S is accum(add,S,0); the product is > accum(multiply,S,1); the minimum is accum(min,S,+infinity); and so > on. Whatever. I'm not actually reusing code here, but I am re-using > a pattern for the umpteenth time since learning it from _The Little > Lisper_ many long years ago. So I copy in the pattern, matching > "emphasis" nodes with a call on "accum" and just leaving two blank > slots for filling in: The one "problem" with this template (and the same was true of Mike's and Dimitre's) is that it's not tail recursive - the recursive call is not the last thing that happens when the template is processed. In general, it's a good idea to try to make recursive templates tail recursive so that processors can treat them in the same way as they would a loop in a procedural programming language - it means they take up less resources, run faster, and don't hit problems with stack depth (with processors that recognise them). Now in this example, there's very little benefit to making the template tail recursive because it's only ever going to recurse three times anyway, but you said you wanted education and it's a general principal that will stand you in good stead for other recursive templates. I find it easier to design a tail-recursive template if I imagine the equivalent loop for what I want to do, which in this case would be: function accum { seq = attributes of emphasis element base = value of emphasis element while (seq) { element name = lookup from first in seq base = <element name> + base + </element name> attributes = rest of seq } return base } In other words, go through the attributes one at a time and update the result by adding the relevant element around the existing result. In the loop, two things get updated, the result and the attributes, so those need to be the parameters: <xsl:template name="accum"> <xsl:param name="seq" select="/.." /> <xsl:param name="base" /> ... </xsl:template> [Note that I defaulted the $seq parameter to an empty node list because that's the kind of value it should take. Not giving a default for the $base parameter is the same as giving it a default of an empty string.] In the template, there's obviously the choice between whether to continue (if there are any nodes in $seq) or not (if there aren't). If there aren't, then the template needs to return a copy of the $base, as passed to it from the preceding recursion (it needs to be a copy to preserve any element structures it might contain): <xsl:template name="accum"> <xsl:param name="seq" select="/.." /> <xsl:param name="base" /> <xsl:choose> <xsl:when test="$seq"> ... </xsl:when> <xsl:otherwise> <xsl:copy-of select="$base" /> </xsl:otherwise> </xsl:choose> </xsl:template> Otherwise, it needs to find the element name and create the relevant element around the existing $base. But this doesn't get returned; this is the new value for the $base parameter on the next recursion: <xsl:template name="accum"> <xsl:param name="seq" select="/.." /> <xsl:param name="base" /> <xsl:choose> <xsl:when test="$seq"> <xsl:variable name="hdvalue"> <xsl:call-template name="lookup-trans"> <xsl:with-param name="in" select="name($seq[1])"/> </xsl:call-template> </xsl:variable> <xsl:call-template name="accum"> <xsl:with-param name="seq" select="$seq[position() > 1]" /> <xsl:with-param name="base"> <xsl:element name="{$hdvalue}"> <xsl:copy-of select="$base" /> </xsl:element> </xsl:with-param> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:copy-of select="$base" /> </xsl:otherwise> </xsl:choose> </xsl:template> Now for the call to the template. The value of the $seq parameter is the set of attributes. The value for the $base parameter is the result of applying templates to the content of the emphasis element - this is the thing that gets copied into the content of whatever elements are generated: <xsl:template match="emphasis"> <xsl:call-template name="accum"> <xsl:with-param name="seq" select="@*"/> <xsl:with-param name="base"> <xsl:apply-templates /> </xsl:with-param> </xsl:call-template> </xsl:template> If the accum template *wasn't* used elsewhere, I'd be tempted to merge the two templates into a single one that matches the emphasis element and has the name 'accum'. The default values for the $seq and $base parameters could then be given the values that are passed in from the emphasis template: <xsl:template match="emphasis" name="accum"> <xsl:param name="seq" select="@*" /> <xsl:param name="base"> <xsl:apply-templates /> </xsl:param> ... </xsl:template> Cheers, Jeni --- Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
[xsl] Re: lookup-table thoughts (wa, Tom Myers | Thread | Re: [xsl] Re: lookup-table thoughts, David Carlisle |
RE: [xsl] Relative path in include , Nouwens, H.J.P. | Date | Re: [xsl] IE6 xml direct browsing, Oleg Tkachenko |
Month |