使用函数对 XSLT 中的变量求和或递增

Sum up or increment a variable in XSLT using a function

我有一个使用 Saxon9.7 和以下 .xsl 文件处理的 .GPX 文件。我尝试通过计算两个跟踪点之间的距离(使用@lon 和@lat 值)来总结不同跟踪点之间的距离。

使用这个函数我尝试递增(或累加)这些值。

函数应该return增加的距离

<xsl:function name="of:gesDistance">
    <xsl:param name="gDistance"/>
    <xsl:param name="distance"/>
    <xsl:value-of select="($gDistance + $distance)"/>
</xsl:function>

我的 .xsl 文件看起来像这样:

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://www.topografix.com/GPX/1/1"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:of ="http://lul.org">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>

<xsl:function name="of:gesDistance">
    <xsl:param name="gDistance"/>
    <xsl:param name="distance"/>
    <xsl:value-of select="($gDistance + $distance)"/>
</xsl:function>

<xsl:template match="trkseg">
    <xsl:variable name="gDistance" select="0"/>
    <xsl:for-each select="trkpt">
        <xsl:variable name="dLat" select="(@lat - preceding-sibling::*[1]/@lat)"/>
        <xsl:variable name="dLon" select="(@lon - preceding-sibling::*[1]/@lon)"/>
        <xsl:variable name="cLon" select="(@lon)"/>
        <xsl:variable name="cLat" select="(@lat)"/>
        <xsl:variable name="pLon" select="(preceding-sibling::*[1]/@lon)"/>
        <xsl:variable name="pLat" select="(preceding-sibling::*[1]/@lat)"/>
        <xsl:variable name="distance" select="6378.388 * math:acos(math:sin($cLat)*math:sin($pLat) + math:cos($cLat) * math:cos($pLat) * math:cos($dLon))"/>
        <xsl:variable name="gDistance" select="of:gesDistance($gDistance, $distance)"/>

        <xsl:text>newNode&#xA;dLat: </xsl:text>
        <xsl:value-of select="$dLat"/>
        <xsl:text>&#xA;dLon: </xsl:text>
        <xsl:value-of select="$dLon"/>
        <xsl:text>&#xA;Distance: </xsl:text>
        <xsl:value-of select="$distance"/>
        <xsl:text> km &#xA;Length: </xsl:text>
        <xsl:value-of select="$gDistance"/>

    <xsl:text>&#xA;</xsl:text>
</xsl:for-each>
</xsl:template>

在 for-each 部分,我总是计算 dLondLat 到前一个跟踪点的距离。稍后计算以公里为单位的距离。

在 for-each 语句之前 xsl:variable gDistance 设置为 0

        <xsl:variable name="gDistance" select="0"/>

下一行描述了使用旧的 gDistance 值和到前一个兄弟的当前距离调用函数的部分。

<xsl:variable name="gDistance" select="of:gesDistance($gDistance, $distance)"/>

运行 使用 Saxon9 得到以下输出:

dLat: -0.0001660000000001105
dLon: -0.0004770000000000607
Distance: 1.1425320012289337 km 
Length: 1.1425320012289337
newNode
dLat: -0.00023200000000400678
dLon: -0.0006450000000004508
Distance: 1.5892769562498525 km 
Length: 1.5892769562498525
newNode
dLat: -0.00023799999999596366
dLon: -0.0004939999999997724
Distance: 1.5814420943745287 km 
Length: 1.5814420943745287

如您所见,总距离始终与前一个兄弟的距离相同。但为什么?有解决我的问题的方法或解决方法吗?

我使用不同的撒克逊命令尝试了不同的方法来解决我的问题,但这也没有用。

是否有准备好的 xml-namespace 具有我需要的功能?

...给你我所有的一切——我的 GPX 文件的一部分

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" creator="" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
 <trk>
  <name>ACTIVE LOG133212</name>
  <trkseg>
   <trkpt lat="48.813909" lon="9.249088">
    <ele>-21.666</ele>
    <time>2008-05-25T13:32:07Z</time>
   </trkpt>
   <trkpt lat="48.814533" lon="9.248918">
    <ele>49.192</ele>
    <time>2008-05-25T13:32:14Z</time>
   </trkpt>
...

我认为如果我们简化示例会更容易。我使用的典型示例是 "bank statement" 问题:给定一系列资金进出账户的交易,显示包含所有交易的银行对账单以及 运行 总金额帐户。

最简单的解决方案是计算

$balance = $initial-balance + sum(preceding-sibling::transaction/value)

但是计算每笔交易的成本是 O(n^2),所以除非交易数量非常小,否则您不想使用此解决方案。

在函数式编程中,有两种主要方法可以解决这个问题。一种是递归:您可以使用递归函数将余额序列计算为交易序列的函数:

<xsl:function name="f:balances" as="xs:decimal*">
   <xsl:param name="initial-balance" as="xs:decimal*"/>
   <xsl:param name="transactions" as="element(transaction)*"/>
   <xsl:variable name="first-balance" select="initial-balance + head(transactions)/value"/>
   <xsl:sequence select="$first-balance"/>
   <xsl:sequence select="f:balances($first-balance, tail($transactions)"/>
</xsl:function>

此函数计算第一笔交易后的余额,然后调用自身计算剩余交易的余额 - 经典 head/tail 递归。

另一种方法是使用折叠操作。折叠操作采用初始值和序列,并将提供的函数依次应用于序列中的每个项目,每次都有一个新的初始值。所以

fn:fold-left($transactions, $initial-balance, function($balance, $transaction) {$balance + $transaction/value})

XSLT 3.0 还引入了 xsl:iterate 作为折叠操作的语法糖:

<xsl:iterate select="$transactions">
  <xsl:param name="initial-balance"/>
  <xsl:variable name="balance" select="./value + $initial-balance"/>
  <xsl:sequence select="$balance"/>
  <xsl:next-iteration>
    <xsl:with-param name="initial-balance" select="$balance"/>
  </xsl:next-iteration>
</xsl:iterate>

如果您觉得这些概念有点令人生畏,那是因为命令式编程已经扭曲了您的大脑,以至于您认为像

这样的语句
i = i + 1

正常且可接受。