xslt cookbook phần 2 ppsx

76 182 0
xslt cookbook phần 2 ppsx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

2.6 Computing Sums and Products 2.6.1 Problem You need to sum or multiply functions of numbers contained in a node set. 2.6.2 Solution The abstract form of sum for processors that support tail-recursive optimization is as follows: <xsl:template name="math:sum"> <! Initialize nodes to empty node set > <xsl:param name="nodes" select="/ "/> <xsl:param name="result" select="0"/> <xsl:choose> <xsl:when test="not($nodes)"> <xsl:value-of select="$result"/> </xsl:when> <xsl:otherwise> <! call or apply template that will determine value of node unless the node is literally the value to be summed > <xsl:variable name="value"> <xsl:call-template name="some-function-of-a-node"> <xsl:with-param name="node" select="$nodes[1]"/> </xsl:call-template> </xsl:variable> <! recurse to sum rest > <xsl:call-template name="math:sum"> <xsl:with-param name="nodes" select="$nodes[position( ) != 1]"/> <xsl:with-param name="result" select="$result + $value"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> Two techniques can handle a large number of nodes in the absence of tail-recursive optimization. The first is commonly called divide and conquer. The idea behind this technique is to reduce the amount of work by at least a factor of two on each recursive step: <xsl:template name="math:sum-dvc"> <xsl:param name="nodes" select="/ "/> <xsl:param name="result" select="0"/> <xsl:param name="dvc-threshold" select="100"/> <xsl:choose> <xsl:when test="count($nodes) &lt;= $dvc-threshold"> <xsl:call-template name="math:sum"> <xsl:with-param name="nodes" select="$nodes"/> <xsl:with-param name="result" select="$result"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:variable name="half" select="floor(count($nodes) div 2)"/> <xsl:variable name="sum1"> <xsl:call-template name="math:sum-dvc"> <xsl:with-param name="nodes" select="$nodes[position( ) &lt;= $half]"/> <xsl:with-param name="result" select="$result"/> <xsl:with-param name="dvc-threshold" select="$dvc- threshold"/> </xsl:call-template> </xsl:variable> <xsl:call-template name="math:sum-dvc"> <xsl:with-param name="nodes" select="$nodes[position( ) > $half]"/> <xsl:with-param name="result" select="$sum1"/> <xsl:with-param name="dvc-threshold" select="$dvc- threshold"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> The second is called batching, which uses two recursive stages. The first stage divides the large problem into batches of reasonable size. The second stage processes each batch recursively. <xsl:template name="math:sum-batcher"> <xsl:param name="nodes" select="/ "/> <xsl:param name="result" select="0"/> <xsl:param name="batch-size" select="500"/> <xsl:choose> <xsl:when test="not($nodes)"> <xsl:value-of select="$result"/> </xsl:when> <xsl:otherwise> <xsl:variable name="batch-sum"> <xsl:call-template name="math:sum"> <xsl:with-param name="nodes" select="$nodes[position( ) &lt; $batch- size]"/> <xsl:with-param name="result" select="$result"/> </xsl:call-template> </xsl:variable> <xsl:call-template name="math:sum-batcher"> <xsl:with-param name="nodes" select="$nodes[position( ) >= $batch-size]"/> <xsl:with-param name="result" select="$batch- sum"/> <xsl:with-param name="batch-size" select="$batch- size"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> The solutions for product are similar: <xsl:template name="math:product"> <xsl:param name="nodes" select="/ "/> <xsl:param name="result" select="1"/> <xsl:choose> <xsl:when test="not($nodes)"> <xsl:value-of select="$result"/> </xsl:when> <xsl:otherwise> <! call or apply template that will determine value of node unless the node is literally the value to be multiplied > <xsl:variable name="value"> <xsl:call-template name="some-function-of-a-node"> <xsl:with-param name="node" select="$nodes[1]"/> </xsl:call-template> </xsl:variable> <xsl:call-template name="math:product"> <xsl:with-param name="nodes" select="$nodes[position( ) != 1]"/> <xsl:with-param name="result" select="$result * $value"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="math:product-batcher"> <xsl:param name="nodes" select="/ "/> <xsl:param name="result" select="1"/> <xsl:param name="batch-size" select="500"/> <xsl:choose> <xsl:when test="not($nodes)"> <xsl:value-of select="$result"/> </xsl:when> <xsl:otherwise> <xsl:variable name="batch-product"> <xsl:call-template name="math:product"> <xsl:with-param name="nodes" select="$nodes[position( ) &lt; $batch-size]"/> <xsl:with-param name="result" select="$result"/> </xsl:call-template> </xsl:variable> <xsl:call-template name="math:product-batcher"> <xsl:with-param name="nodes" select="$nodes[position( ) >= $batch-size]"/> <xsl:with-param name="result" select="$batch- product"/> <xsl:with-param name="batch-size" select="$batch- size"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="math:product-dvc"> <xsl:param name="nodes" select="/ "/> <xsl:param name="result" select="1"/> <xsl:param name="dvc-threshold" select="100"/> <xsl:choose> <xsl:when test="count($nodes) &lt;= $dvc-threshold"> <xsl:call-template name="math:product"> <xsl:with-param name="nodes" select="$nodes"/> <xsl:with-param name="result" select="$result"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:variable name="half" select="floor(count($nodes) div 2)"/> <xsl:variable name="product1"> <xsl:call-template name="math:product-dvc"> <xsl:with-param name="nodes" select="$nodes[position( ) &lt;= $half]"/> <xsl:with-param name="result" select="$result"/> <xsl:with-param name="dvc-threshold" select="$dvc- threshold"/> </xsl:call-template> </xsl:variable> <xsl:call-template name="math:product-dvc"> <xsl:with-param name="nodes" select="$nodes[position( ) > $half]"/> <xsl:with-param name="result" select="$product1"/> <xsl:with-param name="dvc-threshold" select="$dvc- threshold"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> Using the built-in XPath sum( ) function is the simplest way to perform simple sums. However, if you want to compute sums of the nodes' arbitrary function in a node-set, then you need either to: • Use one of the recipes in this section. • Compute the function of the nodes first, capturing the result in a variable as a result-tree fragment. Then use an extension function to convert the fragment to a node set that can be fed to sum. In XSLT 2.0, generalized sums will become trivial because of the banishment of result-tree fragments. 2.6.3 Discussion Batching and divide and conquer are two techniques for managing recursion that are useful whenever you must process a potentially large set of nodes. Experimentation shows that even when using an XSLT processor that recognizes tail recursion, better performance results from these approaches. Chapter 14 shows how to make a reusable batch and divide-and-conquer drivers. 2.6.4 See Also Dimitre Novatchev and Slawomir Tyszko compare batching with divide and conquer at http://www.vbxml.com/xsl/articles/recurse/ 2.7 Finding Minimums and Maximums 2.7.1 Problem You need to find the minimum (or maximum) numerical node (or nodes) in a node set. 2.7.2 Solution The EXSLT functions that perform these operations are math:min, math:max, math:lowest, and math:highest. min and max find the value of the node with minimum and maximum numerical value, respectively. EXSLT defines math:min as follows: The minimum value is defined as follows. The node set passed as an argument is sorted in ascending order as it would be by xsl:sort with a data type of number. The minimum is the result of converting the string value of the first node in this sorted list to a number using the number function. If the node set is empty, or if the result of converting the string values of any of the nodes to a number is NaN, then NaN is returned. math:max is defined similarly. EXSLT provides pure XSLT implementations that are literal implementations of this definition, as shown in Example 2-9. Example 2-9. EXSLT min and max implement directly from the definition <xsl:template name="math:min"> <xsl:param name="nodes" select="/ " /> <xsl:choose> <xsl:when test="not($nodes)">NaN</xsl:when> <xsl:otherwise> <xsl:for-each select="$nodes"> <xsl:sort data-type="number" /> <xsl:if test="position( ) = 1"> <xsl:value-of select="number(.)" /> </xsl:if> </xsl:for-each> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="math:max"> <xsl:param name="nodes" select="/ " /> <xsl:choose> <xsl:when test="not($nodes)">NaN</xsl:when> <xsl:otherwise> <xsl:for-each select="$nodes"> <xsl:sort data-type="number" order="descending" /> <xsl:if test="position( ) = 1"> <xsl:value-of select="number(.)" /> </xsl:if> </xsl:for-each> </xsl:otherwise> </xsl:choose> </xsl:template> You may be scratching your head over the default value for the nodes parameter (select="/ "). This is simply an idiomatic way to initialize nodes to an empty node set (i.e., the parent of the root is empty by definition). Although the definitions of math:min and math:max use xsl:sort, the implementations need not, so results that are more efficient are possible provided your XSLT processor supports tail recursion (see Example 2-10). Example 2-10. min and max implemented with divide and conquer <xsl:template name="math:max"> <xsl:param name="nodes" select="/ "/> <xsl:param name="max"/> <xsl:variable name="count" select="count($nodes)"/> <xsl:variable name="aNode" select="$nodes[ceiling($count div 2)]"/> <xsl:choose> <xsl:when test="not($count)"> <xsl:value-of select="number($max)"/> </xsl:when> <xsl:when test="number($aNode) != number($aNode)"> <xsl:value-of select="number($aNode)"/> </xsl:when> <xsl:otherwise> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="$nodes[not(. &lt;= number($aNode))]"/> <xsl:with-param name="max"> <xsl:choose> <xsl:when test="not($max) or $aNode > $max"> <xsl:value-of select="$aNode"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$max"/> </xsl:otherwise> </xsl:choose> </xsl:with-param> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="math:min"> <xsl:param name="nodes" select="/ "/> <xsl:param name="min"/> <xsl:variable name="count" select="count($nodes)"/> <xsl:variable name="aNode" select="$nodes[ceiling($count div 2)]"/> <xsl:choose> <xsl:when test="not($count)"> <xsl:value-of select="number($min)"/> </xsl:when> <xsl:when test="number($aNode) != number($aNode)"> <xsl:value-of select="number($aNode)"/> </xsl:when> <xsl:otherwise> <xsl:call-template name="math:min"> <xsl:with-param name="nodes" select="$nodes[not(. >= number($aNode))]"/> <xsl:with-param name="min"> <xsl:choose> <xsl:when test="not($min) or $aNode &lt; $min"> <xsl:value-of select="$aNode"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$min"/> </xsl:otherwise> </xsl:choose> </xsl:with-param> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> Typically, the preceding implementations are faster than the version using xsl:sort. In some degenerate cases, they are likely to be slower. The reason is that efficiency hinges on removing half the nodes from consideration (on average) at each recursive step. One can imagine a scenario in which at each pass aNode is the minimum node (in the case of math:max) or the maximum remaining node (in the case of math:min). If this were to occur, each pass would remove only one node, resulting in poor performance. Luckily, data tends to come in two configurations: presorted and random. In both cases, these implementations should hold their own. While you had to look out for non-numeric data explicitly, EXSLT's implementations let xsl:sort take care of this. The XSLT standard mandates that non-numeric data be placed up front by sort when data-type='number'. Don't be tempted to write not(number($var)) to test for NaN! I often catch myself doing this because it "sounds" correct. The number function does not test for a number; instead, it attempts to convert its argument to a number. This is not what you want—this test will conclude 0 is not a number due to the conversion of 0 to false. The correct test is number($var) != number($var). This test works because NaN is never equal to NaN, but any number is always equal to itself. Do not be tempted to shorten this idiom to number($var) != $var. Doing so works most of the time, but if $var is an empty node set, it will fail. If you prefer, a more direct approach string(number($var)) = 'NaN' also works. EXSLT defines math:lowest as follows. The math:lowest function returns the nodes in the node set whose value is the minimum value for the node set. The minimum value for the node set is the same as the value as calculated by math:min. A node has this minimum value if the result of converting its string value to a number as if by the number function is equal to the minimum value, where the equality comparison is defined as a numerical comparison using the = operator. If any of the nodes in the node set has a non-numeric value, the math:min function will return NaN. The definition numeric comparisons entails that NaN != NaN. Therefore if any of the nodes in the node set has a non-numeric value, math:lowest will return an empty node set. The EXSLT implementation is literally based on this definition and might not be very efficient: <xsl:template name="math:lowest"> <xsl:param name="nodes" select="/ " /> <xsl:if test="$nodes and not($nodes[number(.) != number(.)])"> <xsl:variable name="min"> <xsl:for-each select="$nodes"> <xsl:sort data-type="number" /> <xsl:if test="position( ) = 1"> <xsl:value-of select="number(.)" /> </xsl:if> </xsl:for-each> </xsl:variable> <xsl:copy-of select="$nodes[. = $min]" /> </xsl:if> </xsl:template> The xsl:if test scans all nodes to handle cases when a non-numeric is present. It then sorts to find the min and finally collects all nodes with that min. Example 2-11 reuses math:min to do the same without needing to sort. Example 2-11. Lowest impleme nted by reusing math:min <xsl:template name="math:lowest"> <xsl:param name="nodes" select="/ "/> <xsl:variable name="min"> <xsl:call-template name="math:min"> <xsl:with-param name="nodes" select="$nodes"/> </xsl:call-template> </xsl:variable> <xsl:choose> <xsl:when test="number($min) = number($min)"> <xsl:copy-of select="$nodes[. = $min]"/> </xsl:when> </xsl:choose> </xsl:template> Finally, you can implement math:lowest with only one pass over the nodes if you are willing to forego reuse of math:min (see Example 2-12). Example 2-12. Lowest implemented without reuse of math:min <xsl:template name="math:lowest"> <xsl:param name="nodes" select="/ "/> <xsl:param name="lowest" select="/ "/> <xsl:variable name="index" select="ceiling(count($nodes) div 2)"/> <xsl:variable name="aNode" select="$nodes[$index]"/> <xsl:choose> <xsl:when test="not($index)"> <xsl:copy-of select="$lowest"/> </xsl:when> <xsl:when test="number($aNode) != number($aNode)"/> <xsl:otherwise> <xsl:choose> <xsl:when test="not($lowest) or $aNode &lt; $lowest"> <xsl:call-template name="math:lowest"> <xsl:with-param name="nodes" select="$nodes[not(. >= $aNode)]"/> <xsl:with-param name="lowest" select="$nodes[. = $aNode]"/> </xsl:call-template> </xsl:when> <xsl:when test="$aNode = $lowest"> <xsl:call-template name="math:lowest"> <xsl:with-param name="nodes" select="$nodes[not(. >= $aNode)]"/> <xsl:with-param name="lowest" select="$lowest|$nodes[$index]"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:call-template name="math:lowest"> <xsl:with-param name="nodes" select="$nodes[not(. >= $aNode)]"/> <xsl:with-param name="lowest" select="$lowest"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:otherwise> </xsl:choose> </xsl:template> Interestingly, this implementation does worse, probably because of the additional copying that occurs. In performance tests on 10,000 data points using various distributions of data (sorted, reverse sorted, semirandom, and random), the math:min-based implementation beat the xsl:sort-based implementation by about 40% on average (often better). The recursive implementation that did not use math:min was 24% slower than the one that did. The math:highest definition and implementations follow directly from inverting the relational logic of math:lowest, so I will not discuss them here. 2.7.3 Discussion [...]... test="$num1 < 1 and $num2 < 1"> ... test="$num1 < 1 and $num2 < 1"> ... name="bit13" name="bit 12" name="bit11" name="bit10" name="bit9" name="bit8" name="bit7" name="bit6" name="bit5" name="bit4" name="bit3" name="bit2" name="bit1" name="bit0" select=" 327 68"/> select="16384"/> select="81 92" /> select="4096"/> select= "20 48"/> select="1 024 "/> select="5 12" /> select= "25 6"/> select=" 128 "/> select="64"/> select=" 32" /> select="16"/> select="8"/> select="4"/> select= "2" /> select="1"/>... ... select="floor($num1 div 2) "/> ... select="$nodes[ceiling($count1 div 2) ]"/> Chapter 3 Dates and Times Does anyone really know what time it is? Does anyone really care? —Chicago 3.1 Introduction Native XSLT 1.0 does not know... . http://www.vbxml.com/xsl/articles/recurse/ 2. 7 Finding Minimums and Maximums 2. 7.1 Problem You need to find the minimum (or maximum) numerical node (or nodes) in a node set. 2. 7 .2 Solution The EXSLT functions that. math:max is defined similarly. EXSLT provides pure XSLT implementations that are literal implementations of this definition, as shown in Example 2- 9. Example 2- 9. EXSLT min and max implement directly. $num1"/> <xsl:variable name="nextN2" select="($num2 >= $test) * ($num2 - $test) + not($num2 >= $test) * $num2"/> <xsl:choose> <xsl:when

Ngày đăng: 14/08/2014, 01:20

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan