在XPath 2.0中表达集合相等的惯用方法

时间:2015-03-25 20:44:47

标签: xslt xslt-2.0 xpath-2.0

如果$ A和$ B是序列,那么测试$ A和$ B的集合平等的首选方法是什么?我知道($A = $B)的存在语义行为使得这个表达式不是的答案。 deep-equal()的排序语义也禁止我使用它。

我的冲动是使用:

((every $a in $A satisfies $a = $B) and (every $b in $B satisfies $b = $A))

我发现通过谷歌进行集合平等测试的确很少(确切地说,我发现什么都没有),我在@Michael-Kay&#中没有看到它提到的#&} 39;第8章,第9章,第10章或第13章。很难相信我是第一个遇到这种需求的XPath用户。这让我想知道我是否提出了错误的问题。

2 个答案:

答案 0 :(得分:3)

一个有趣且问题很好的问题!在我看来,使用everysatisfies来克服序列比较的存在性属性是一种非常有效且规范的方法。

但是,既然你问的是另一种方法:在将序列与deep-equal()进行比较之前如何对序列进行排序?让我们假设以下样式表中有两个序列:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="text"/>

    <xsl:variable name="A" select="('a','b','c')"/>
    <xsl:variable name="B" select="('a','c','b')"/>

    <xsl:template match="/">
      <xsl:choose>
          <xsl:when test="deep-equal($A,$B)">
              <xsl:text>DEEP-EQUAL!</xsl:text>
          </xsl:when>
          <xsl:otherwise>
              <xsl:text>NOT DEEP-EQUAL</xsl:text>
          </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

</xsl:transform>

当然,这种转变会返回

NOT DEEP-EQUAL

但如果您在比较序列之前对序列进行排序,例如使用使用xsl:perform-sort的自定义函数,请参阅relevant part of the specification

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:local="www.local.com" extension-element-prefixes="local"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="text"/>

    <xsl:variable name="A" select="('a','b','c')"/>
    <xsl:variable name="B" select="('a','c','b')"/>

    <xsl:template match="/">
      <xsl:choose>
          <xsl:when test="deep-equal(local:sort($A),local:sort($B))">
              <xsl:text>DEEP-EQUAL!</xsl:text>
          </xsl:when>
          <xsl:otherwise>
              <xsl:text>NOT DEEP-EQUAL</xsl:text>
          </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

    <xsl:function name="local:sort" as="xs:anyAtomicType*">
        <xsl:param name="in" as="xs:anyAtomicType*"/>
        <xsl:perform-sort select="$in">
            <xsl:sort select="."/>
        </xsl:perform-sort>
    </xsl:function>

</xsl:transform>

然后,结果将是

DEEP-EQUAL!

编辑:事实上,设置相等会导致不仅订单无关紧要,而且重复也不会产生影响。因此,正确的集合相等意味着也将distinct-values()应用于序列变量:

<xsl:when test="deep-equal(local:sort(distinct-values($A)),local:sort(distinct-values($B)))">

答案 1 :(得分:2)

首先,它取决于您如何评估项目之间的平等,例如,您使用&#34; =&#34;,&#34;是&#34;,&#34; eq&#34;来比较两个项目? ,或者&#34;深度平等&#34;?你自己的答案表明你正在考虑&#34; =&#34;,这几乎与&#34; eq&#34;相同。应用于项目时,除了转换规则略有不同。并且当它发生时,它不适合用于集合,因为它不具有传递性:untypedAtomic(&#34; 4&#34;)= 4,untypedAtomic(&#34; 4&# 34;)=&#34; 4&#34;,但不是(4 =&#34; 4&#34;)。因此,让我们假设&#34; eq&#34;这是传递性的,除了涉及数值四舍五入的极端情况,几乎相等&#34;。

然后我倾向于建议(正如其他人所做的那样)

deep-equal(sort($A), sort($B)) 

除了某些数据类型(例如QNames)定义了相等运算符,但没有定义排序。所以这适用于整数和字符串,但不适用于QNames。

显然,在你的&#34; impulse&#34;中给出了O(n ^ 2)方法。解决方案,但可以做得更好吗?

怎么样

let $A := count(distinct-values($a))
let $B := count(distinct-values($b))
let $AB := count(distinct-values(($a, $b))
return $A = $AB and $B = $AB

这应该是O(n log n)。