将两个数字相乘然后相加
Posted
技术标签:
【中文标题】将两个数字相乘然后相加【英文标题】:Multiply 2 numbers and then sum 【发布时间】:2010-10-01 00:31:38 【问题描述】:我很难尝试做一些看起来应该很容易做的事情。我基本上想将一个节点中的 2 个数字相乘,然后将所有节点的这些数字相加。这是我尝试过的 XSLT 代码。
<xsl:value-of select="sum(Parts/Part/Quantity * Parts/Part/Rate)"/>
此代码导致错误提示“函数 sum 的参数 1 无法转换为节点集”。
有没有人知道哪里出了问题或者我如何才能完成我想做的事情?
【问题讨论】:
你应该提到足够的细节..以便其他人能想到帮助你..应该提到 XSLT 版本、处理器(saxon 或其他)或@Least 软件(Visual Studio/Altova XML Spy) .. 【参考方案1】:以下是三种可能的解决方案:
解决方案 1 XSLT2:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:sequence select="sum(/*/*/(rate * quantity))"/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于以下 XML 文档时:
<parts>
<part>
<rate>0.37</rate>
<quantity>10</quantity>
</part>
<part>
<rate>0.03</rate>
<quantity>10</quantity>
</part>
</parts>
产生想要的结果:
4
XSLT 2.0 解决方案使用了这样一个事实:在XPath 2.0 中,允许最后一个“/”运算符的右参数可以是表达式或通常是函数。这个表达式/函数被应用到目前选择的作为上下文节点的每个节点,每个函数应用都会产生一个结果。
解决方案2 XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="sumProducts">
<xsl:with-param name="pList" select="*/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="sumProducts">
<xsl:param name="pList"/>
<xsl:param name="pAccum" select="0"/>
<xsl:choose>
<xsl:when test="$pList">
<xsl:variable name="vHead" select="$pList[1]"/>
<xsl:call-template name="sumProducts">
<xsl:with-param name="pList" select="$pList[position() > 1]"/>
<xsl:with-param name="pAccum"
select="$pAccum + $vHead/rate * $vHead/quantity"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pAccum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
当应用于上述 XML 文档时,会产生正确的结果:
4
这是一个典型的 XSLT 1.0 递归解决方案。 请注意sumProducts
模板如何递归调用自身,直到处理传入参数$pList
的整个输入列表。
解决方案 3 FXSL (XSLT 1.0):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:test-map-product="test-map-product"
exclude-result-prefixes="xsl ext test-map-product"
>
<xsl:import href="sum.xsl"/>
<xsl:import href="map.xsl"/>
<xsl:import href="product.xsl"/>
<!-- This transformation is to be applied on:
salesMap.xml
It contains the code of the "sum of products" from the
article "The Functional Programming Language XSLT"
-->
<test-map-product:test-map-product/>
<xsl:output method="text"/>
<xsl:template match="/">
<!-- Get: map product /sales/sale -->
<xsl:variable name="vSalesTotals">
<xsl:variable name="vTestMap" select="document('')/*/test-map-product:*[1]"/>
<xsl:call-template name="map">
<xsl:with-param name="pFun" select="$vTestMap"/>
<xsl:with-param name="pList1" select="/sales/sale"/>
</xsl:call-template>
</xsl:variable>
<!-- Get sum map product /sales/sale -->
<xsl:call-template name="sum">
<xsl:with-param name="pList" select="ext:node-set($vSalesTotals)/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="makeproduct" match="*[namespace-uri() = 'test-map-product']">
<xsl:param name="arg1"/>
<xsl:call-template name="product">
<xsl:with-param name="pList" select="$arg1/*"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
当此转换应用于以下 XML 文档时:
<sales>
<sale>
<price>3.5</price>
<quantity>2</quantity>
<Discount>0.75</Discount>
<Discount>0.80</Discount>
<Discount>0.90</Discount>
</sale>
<sale>
<price>3.5</price>
<quantity>2</quantity>
<Discount>0.75</Discount>
<Discount>0.80</Discount>
<Discount>0.90</Discount>
</sale>
</sales>
产生正确的结果:
7.5600000000000005
在最后一种情况下,对于每个sale
,我们计算price
、quantity
和所有可用(可变数量)discount
-s 的乘积。
FXSL 是高阶函数的纯 XSLT 实现。在此示例中,使用高阶函数 f:map()
将函数 f:product()
映射到每个 sale
元素的子列表中。然后将结果相加得出最终结果。
【讨论】:
谢谢。解决方案 #2 正是我创建聚合乘法函数所需要的。 @infant-programmer:感谢您的赞赏!如果你喜欢这个,你会喜欢我博客上的很多东西:dnovatchev.spaces.live.com/Blog【参考方案2】:Dimitre 的所有解决方案都有效,他说得对,您需要使用扩展功能,但有时它会让生活更轻松。这并不太有害,尤其是当您使用跨多个 XSLT 处理器支持的 exslt 扩展时。此外,您收到序列错误的原因可能是因为您使用的是 XSLT 1 处理器。
如果您想坚持使用您选择的解决方案,则需要使用 Saxon 或其他一些支持 XSLT 2 的 XSLT 处理器。
否则,这是在 XSLT 1 中执行此操作的另一种方法。这将适用于大多数 XSLT 处理器,并且某些人可能会发现它比递归版本更容易理解。就个人而言,我更喜欢递归版本(Dimitre 的第三个提案),因为它更便携。
<xsl:stylesheet version="1.0"
xmlns:ex="http://exslt.org/common"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template name="GetProducts">
<xsl:param name="left"/>
<xsl:param name="right"/>
<xsl:for-each select="$left/text()">
<product>
<xsl:value-of select="number(.) * number($right[position()])"/>
</product>
</xsl:for-each>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="products">
<xsl:call-template name="GetProducts">
<xsl:with-param name="left" select="Parts/Part/Rate"/>
<xsl:with-param name="right" select="Parts/Part/Quantity"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="sum(ex:node-set($products)/product)"/>
</xsl:template>
</xsl:stylesheet>
【讨论】:
以上是关于将两个数字相乘然后相加的主要内容,如果未能解决你的问题,请参考以下文章