【问题标题】:XSLT 1.0 accumulation, summing, multiplicationXSLT 1.0 累加、求和、乘法
【发布时间】:2010-12-23 22:43:10
【问题描述】:

我有一组固定的小节点:<a><b><c><d>。每个节点的值可以是 1 或 0。 此外,每个节点都有一个权重:分别为 1、2、3、4。不使用节点属性。 如何使用 XSLT 1.0 将每个节点的值乘以其权重相加?示例:

<a>0</a>
<b>1</b>
<c>0</c>
<d>1</d>

总和:6

<a>1</a>
<b>1</b>
<c>0</c>
<d>0</d>

总和:3

<a>0</a>
<b>1</b>
<c>1</c>
<d>1</d>

总和:9

【问题讨论】:

  • 好问题,+1。有关完整而简短的 XSLT 1.0 解决方案,请参阅我的答案。 :)

标签: xslt xslt-1.0


【解决方案1】:

这个 XSLT 1.0 转换

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <xsl:call-template name="sumWeighted"/>
 </xsl:template>

 <xsl:template name="sumWeighted">
   <xsl:param name="pList" select="*"/>
   <xsl:param name="pIndex" select="1"/>
   <xsl:param name="pAccum" select="0"/>

   <xsl:choose>
    <xsl:when test="$pIndex > count($pList)">
     <xsl:value-of select="$pAccum"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="sumWeighted">
        <xsl:with-param name="pList" select="$pList"/>
        <xsl:with-param name="pIndex"
             select="$pIndex+1"/>
        <xsl:with-param name="pAccum"
             select="$pAccum+$pList[$pIndex]*$pIndex"/>
      </xsl:call-template>
    </xsl:otherwise>
   </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<t>
    <a>0</a>
    <b>1</b>
    <c>0</c>
    <d>1</d>
</t>

,

<t>
    <a>1</a>
    <b>1</b>
    <c>0</c>
    <d>0</d>
</t>

<t>
    <a>0</a>
    <b>1</b>
    <c>1</c>
    <d>1</d>
</t>

分别产生想要的结果

6

3

9


参考

如果您想亲自动手使用 XSLT 进行非常复杂的数学计算,请参阅:

http://fxsl.sourceforge.net/articles/xslCalculator/The%20FXSL%20Calculator.html


XPath 2.0 / XSLT 2.0 解决方案

只使用这个 XPath 2.0 单行:

   sum(/*/*/(number()*position()))

【讨论】:

  • @Dimitre,你也可以批评我的解决方案吗?我真的很感激。
  • @Flack:您的解决方案很好。它比必要的要冗长一些。特别是,权重不是必需的,因为每个权重只是元素的位置。
  • @Dimitre,我认为权重可以改变的假设并非完全没有意义。虽然在本主题中不需要。
  • @Flack:正确。就我自己而言,我总是想专注于问题的本质,而不是提供可能分散注意力和混淆的额外内容。这就是为什么我的答案有它现在的形式。我作为参考提供的链接可能包含人们能想到的最复杂的数学计算。因此,如果有人有兴趣深入了解,那么他们会点击链接。
  • @Dimitre,我不是在评判,实际上是赞成 :) 我只是想为自己更进一步,只是为了好玩。
【解决方案2】:

不是很优雅,但这就是我想到的。

<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my"
exclude-result-prefixes="my">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>

<my:node-weight>
    <node name="a" weight="1"/>
    <node name="b" weight="2"/>
    <node name="c" weight="3"/>
    <node name="d" weight="4"/>
</my:node-weight>

<xsl:template match="root">
    <xsl:apply-templates select="*[1]" mode="sum"/>
</xsl:template>

<xsl:template match="*" mode="sum">
    <xsl:param name="sumNumber" select="0"/>
    <xsl:variable name="thisWeight" select="document('')/*/my:node-weight/node[@name = local-name(current())]/@weight"/>
    <xsl:choose>
        <xsl:when test="following-sibling::*">
            <xsl:apply-templates select="following-sibling::*[1]" mode="sum">
                <xsl:with-param name="sumNumber" select="$sumNumber + $thisWeight * number()"/>
            </xsl:apply-templates>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$sumNumber + $thisWeight * number()"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

当应用于以下格式良好的文档时:

<root>
    <a>0</a>
    <b>1</b>
    <c>0</c>
    <d>1</d>
</root>

结果是6

<root>
    <a>1</a>
    <b>1</b>
    <c>0</c>
    <d>0</d>
</root>

结果是3

<root>
    <a>0</a>
    <b>1</b>
    <c>1</c>
    <d>1</d>
</root>

结果是9

【讨论】:

  • +1 我喜欢细粒度的遍历。但是除了重量可能只是一个计数器之外,我会为下一个节点使用一个变量并计算避免重复代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-14
  • 1970-01-01
  • 1970-01-01
  • 2016-02-28
  • 1970-01-01
  • 2016-12-03
相关资源
最近更新 更多