Jeder Topf hat vier Eigenschaften:
- verschiedene Deckel: kein Deckel, flacher Griff, hoher Griff
- verschiedene Farben: hell, gestreift, dunkel
- verschiedene Zeichen: Ring, Kreuz, Doppelkreuz
- verschiedene Zeichenanzahl: 1, 2 oder 3
Es sollen genau drei Töpfe so ausgewählt werden, dass für jede der vier Eigenschaften alle Töpfe entweder den gleichen Wert oder alle unterschiedliche Werte haben.
XML-Notation
Die Eigenschaften sind Attribute (zur Vereinfachung nenne ich diese a, b, c und d) und jedes Attribut muss einen von drei möglichen Werten annehmen (1, 2, 3).
<pots>
<pot a="1" b="1" c="3" d="2"></pot>
<pot a="1" b="1" c="3" d="3"></pot>
<pot a="3" b="2" c="1" d="2"></pot>
<pot a="1" b="3" c="2" d="1"></pot>
<pot a="1" b="1" c="1" d="1"></pot>
<pot a="2" b="2" c="1" d="2"></pot>
<pot a="3" b="2" c="2" d="1"></pot>
<pot a="3" b="2" c="1" d="1"></pot>
<pot a="3" b="1" c="3" d="2"></pot>
<pot a="3" b="1" c="1" d="1"></pot>
<pot a="2" b="1" c="3" d="2"></pot>
<pot a="3" b="2" c="3" d="3"></pot>
</pots>
Lässt sich die Lösung mit einem XPath-Ausdruck angeben? Nein, wohl nicht, und ich bin auch nur auf eine brute force-Lösung gekommen, die mir wenig elegant erscheint:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my"
exclude-result-prefixes="#all">
<xsl:output indent="yes" />
<xsl:template match="pots">
<xsl:variable name="a" as="node()*" select="my:triples(*, 'a')" />
<xsl:variable name="b" as="node()*" select="my:triples(*, 'b')" />
<xsl:variable name="c" as="node()*" select="my:triples(*, 'c')" />
<xsl:variable name="d" as="node()*" select="my:triples(*, 'd')" />
<result>
<!-- only triples which are present for all attributes are returned -->
<xsl:for-each select="$a[. = $b][. = $c][. = $d]">
<xsl:sequence select="." />
</xsl:for-each>
</result>
</xsl:template>
<!-- this function returns all valid triples for a single attribute -->
<xsl:function name="my:triples" as="node()*">
<xsl:param name="nodes" as="node()+" />
<xsl:param name="attr" as="xs:string" />
<!-- look for common values -->
<xsl:for-each select="$nodes">
<xsl:variable name="t1" select="." />
<xsl:for-each select="$t1/following-sibling::*[@*[name() eq $attr] eq $t1/@*[name() eq $attr]]">
<xsl:variable name="t2" select="." />
<xsl:for-each select="$t2/following-sibling::*[@*[name() eq $attr] eq $t1/@*[name() eq $attr]]">
<triple>
<xsl:value-of select="(my:pos($nodes, $t1), my:pos($nodes, $t2), my:pos($nodes, .))" />
</triple>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<!-- look for distinct values -->
<xsl:for-each select="$nodes">
<xsl:variable name="t1" select="." />
<xsl:for-each select="$t1/following-sibling::*[@*[name() eq $attr] ne $t1/@*[name() eq $attr]]">
<xsl:variable name="t2" select="." />
<xsl:for-each select="
$t2/following-sibling::*[@*[name() eq $attr] ne $t1/@*[name() eq $attr]
and
@*[name() eq $attr] ne $t2/@*[name() eq $attr]]">
<triple>
<xsl:value-of select="(my:pos($nodes, $t1), my:pos($nodes, $t2), my:pos($nodes, .))" />
</triple>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:function>
<xsl:function name="my:pos" as="xs:string">
<xsl:param name="nodes" as="node()+" />
<xsl:param name="node" as="node()" />
<xsl:value-of select="count($nodes[$node >> .]) + 1" />
</xsl:function>
</xsl:transform>
Wer kann eine elegantere Lösung anbieten?