Subject: Re: [xsl] stack usage in for-each and apply-templates (was Re: [xsl] Is xsl:for-each "syntactic sugar"?) From: Dimitre Novatchev <dnovatchev@xxxxxxxxx> Date: Fri, 7 May 2010 12:02:41 -0700 |
On Fri, May 7, 2010 at 10:55 AM, C. M. Sperberg-McQueen <cmsmcq@xxxxxxxxxxxxxxxxx> wrote: > On 6 May 2010, at 19:49 , Dimitre Novatchev wrote: > >>> 4. Favor recursive functions over xsl:for-each. (True or False) > >> No, Always choose iterative processing over recursion, if this is >> possible. You may gain significant efficiency this way. > > True. > > But if we are going to allow efficiency of execution to play a role > in our choice (it's not always wrong to do so, I guess!), then in > the context of XSLT it's important to note that the choice between > for-each and apply-templates with a mode (along the lines suggested > by David Carlisle) is not a choice between iterative processing and > recursion: both constructs are iterative in XSLT. This is just a clarification that I used the word "recursion", not "apply-templates". And using <xsl:apply-templates> is not automatically synonymous to recursion. Only when we have <xsl:apply-templates> evaluated *within* a template (that itself was instantiated explicitly or implicitly by an <xsl:apply-templates> or <xsl:call-template>) this *may* lead to recursion if this causes directly or indirectly the same template to be evaluated further down the call graph. As for the rest of the text of . M. Sperberg-McQueen's message, there is probably nothing more to argue about -- these are wellknown facts. Also it is a fact that not all XSLT implementations recognize/optimize tail-recursion, so if we are writing a portable XSLT application and want it to be efficient when processing more or less large XML documents, we have to resort either to DVC-style recursion or to try to avoid recursion, if possible. -- 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 > > In imperative languages, it's different. B If you iterate over the > integers from 1 to 1000000 using a tail-recursive function of the > form > > B (defun f (i) > B B B B ... body ... > B B B B (f (+1 i))) > > or > > B declare function f ( $i : xs:int) as xs:int { > B B ... body ... > B B if ... then > B B B return $result > B B else > B B B return f($i + 1) > B } > > then the stack will grow as the recursion proceeeds, and in the > absence of tail-recursion optimization you are going to end up > trying to push a million stack frames onto the execution stack, > which means you are likely to run out of stack space. B And in > the imperative languages I know, those are your two choices: > iterative constructs or iteration by means of tail-recursive > functions. > > It's possible to write a similar style of (tail-) recursive > template in XSLT, of course. B (I'm not telling D.N. anything > he doesn't know, of course.) > > As far as I can tell the main reason (maybe the only reason?) B to do > it is to pass some value along in a parameter that serves as a kind > of accumulator, and finally, when the recursion ends, to pass back > the accumulated value, so that the pattern I have in mind is > something like this: > > B <xsl:apply-templates select="first-node-of-nodeset-foo" > B B mode="s.w.a.b."/> > > B <xsl:template match="*" mode="s.w.a.b."> > B B <xsl:param name="accumulator" select="0"/> > > B B ... body ... > > B B <xsl:choose> > B B B <xsl:when test="... termination condition ... "> > B B B B <xsl:value-of select="$new-accum"/> > B B B </xsl:when> > B B B <xsl:otherwise> > B B B B <xsl:apply-templates > B B B B B select="next-node-of-nodeset-foo" > B B B B B mode="s.w.a.b."> > B B B B B B <xsl:with-param > B B B B B B B name="accumulator" select="$new-accum"/> > B B B B </xsl:apply-templates> > B B B </xsl:otherwise> > B B </xsl:choose> > B </xsl:template> > > (The mode name 's.w.a.b.' stands for 'Scheme with angle brackets' > and is intended to amuse those readers who may find it amusing. B The > rest of you can stop groaning now, please. Thank you.) > > For this, you really do need tail-recursion optimization or a lot of > stack space, at least in principle. B (In practice, when I have > needed this pattern it has run without any trouble at all, perhaps > because my input documents were not large. B The common view seems to > be that this will always fail on anything but artificially small > examples; in reality, it has always worked for me.) > > Two observations arise here: > > (1) I don't know how to get the effect of the accumulator with > xsl:for-each. B (But just wait for XSLT 2.1 and the xsl:iterate > instruction!) > > Is it possible to do accumulator-passing in for-each? > > (2) As David Carlisle has pointed out, if you take a for-each > construct of the form > > B <xsl:for-each select="foo"> > B B ... body ... > B </xsl:for-each> > > the equivalent apply-templates construction does not resemble the > s.w.a.b. example above, but instead has > > B <xsl:apply-templates select="foo" mode="unique-mode-id"/> > > and elsewhere > > B <xsl:template match="*" mode="unique-mode-id"> > B B <!--* or match="@*|/|node()" if you like, but let's > B B B B * assume we are matching only elements > B B B B *--> > B B ... body ... > B </xsl:template> > > This iterates over the elements which match the select pattern, and > the resulting stack usage will be that > > B a) A node-set stack frame F1 goes on the stack. > > B b) A node N is selected from the node set (this is done for > B B each node in F1 in turn); a template T is found for the node N. > B B If the node set has no nodes, or none that have yet been > B B selected, then we go to step f). > > B c) A template frame F2 is added to the stack for the evaluation > B B of template T with context node N, > > B d) Template T is evaluated with context node N. B Here, the > B B stack may grow owing to instructions in T, just as it may grow > B B in the for-each case owing to the instructions in the body of > B B the for-each. B After all, they contain the same body. B Any > B B stack growth caused by constructs in the body will be the same > B B for for-each and for apply-templates, so that's a wash. B At the > B B end of the evaluation, template frame F2 will once again be at > B B the top of the stack. > > B e) The template frame F2 is popped from the stack, so the > B B node-set frame F1 will be at the top of the stack again. B Go to > B B step b). > > B f) The node set frame F1 is popped from the stack. > > If the node set we are concerned with has K members, then the loop > from b) to e) will be run once for each node in the node set > selected, but even without tail-recursion optimization, the stack > will grow by 1 item, not by K items. B Contrast the s.w.a.b. example, > in which you do get an increase in stack depth of K frames (or in a > really naive implementation K*2, since each recursion will push both > a singleton node-set and a template frame onto the stack). > > I'm assuming a fairly naive pattern of stack usage here; it's > possible to improve it in various ways. B But an implementation > would have to go a long way out of its way to make a single call > to apply-templates, with a select expression matching K nodes, > increase stack depth by K instead of by 1. > > There may be good reasons to use for-each. B It may for example be > better optimized than an apply-templates with a mode (but if the > mode has only a single template, which matches everything, just how > slow can the processor make the search for the appropriate > template?). B Some people may for example find for-each clearer > (although in my experience some programmers who find for-each > clearer are not comfortable in XSLT in the first place). > > But difference in stack usage is NOT a reason to choose for-each > over apply-templates. > > -- > **************************************************************** > * C. M. Sperberg-McQueen, Black Mesa Technologies LLC > * http://www.blackmesatech.com > * http://cmsmcq.com/mib > * http://balisage.net > ****************************************************************
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
[xsl] stack usage in for-each and a, C. M. Sperberg-McQue | Thread | RE: [xsl] Is xsl:for-each "syntacti, Markus Karg |
Re: [xsl] Is xsl:for-each "syntacti, C. M. Sperberg-McQue | Date | Re: [xsl] RE: Is xsl:for-each "synt, David Carlisle |
Month |