8

半順序を表す有向非巡回グラフ (DAG)をエンコードする XML ファイルがあります 。このようなグラフは、依存関係の指定やクリティカル パスの検索などに役立ちます。興味深いことに、私の現在のアプリケーションはビルド システムのコンポーネントの依存関係を指定することなので、頂点はコンポーネントであり、エッジはコンパイル時の依存関係を指定します。簡単な例を次に示します。

<?xml version="1.0"?>
<dag>
    <vertex name="A">
        <directed-edge-to vertex="C"/>
    </vertex>
    <vertex name="B">
        <directed-edge-to vertex="C"/>
        <directed-edge-to vertex="D"/>
    </vertex>
    <vertex name="C">
        <directed-edge-to vertex="E"/>
    </vertex>
    <vertex name="D">
        <directed-edge-to vertex="E"/>
    </vertex>
    <vertex name="E">
        <directed-edge-to vertex="G"/>
    </vertex>
    <vertex name="F">
        <directed-edge-to vertex="G"/>
    </vertex>
    <vertex name="G"/>
</dag>

この DAG は次のように描画できます。


(出典:iparelan.com

半順序の最小要素に対応する頂点のみを含む別の XML ドキュメントを生成するXSLT スタイルシートを適用したいと考えています。つまり、着信エッジを持たない頂点です。サンプル グラフの最小頂点のセットは です。私のビルド依存関係アプリケーションでは、このセットを見つけることは価値があります。なぜなら、このセットのメンバーをビルドすると、プロジェクト内のすべてがビルドされることがわかっているからです。{A, B, F}

これが私の現在のスタイルシート ソリューションです (Apache Ant のxsltタスクを使用して、Java 上の Xalan でこれを実行しています)。重要な観察事項は、最小頂点はどのdirected-edge-to要素でも参照されないということです。

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xalan="http://xml.apache.org/xslt"
                exclude-result-prefixes="xalan">
    <xsl:output method="xml" indent="yes" xalan:indent-amount="4"/>

    <xsl:template match="dag">
        <minimal-vertices>
            <xsl:for-each select="//vertex">
                <xsl:if test="not(//vertex/directed-edge-to[@vertex=current()/@name])">
                    <minimal-vertex name="{@name}"/>
                </xsl:if>
            </xsl:for-each>
        </minimal-vertices>
    </xsl:template>
</xsl:stylesheet>

このスタイルシートを適用すると、次の出力が生成されます (これは正しいと思います)。

<?xml version="1.0" encoding="UTF-8"?>
<minimal-vertices>
    <minimal-vertex name="A"/>
    <minimal-vertex name="B"/>
    <minimal-vertex name="F"/>
</minimal-vertices>

問題は、私はこのソリューションに完全に満足していないということです。を XPath 構文と組み合わせる方法があるかどうか疑問に思っています。selectfor-eachtestif

私は次のようなものを書きたい:

<xsl:for-each select="//vertex[not(//vertex/directed-edge-to[@vertex=current()/@name])]">

current()しかし、関数は外側の//vertex式によって選択されたノードを参照しないため、それは私が望むことではありません。

これまでのところ、私のソリューションではXPath 1.0XSLT 1.0の構文を使用していますが、XPath 2.0XSLT 2.0の構文も受け入れています。

必要に応じて、Ant ビルド スクリプトを次に示します。

<?xml version="1.0"?>
<project name="minimal-dag" default="default">
    <target name="default">
        <xslt in="dag.xml" out="minimal-vertices.xml" style="find-minimal-vertices.xsl"/>
    </target>
    <target name="dot">
        <xslt in="dag.xml" out="dag.dot" style="xml-to-dot.xsl"/>
    </target>
</project>

dotターゲットは、グラフをレンダリングするための Graphviz Dot 言語コードを生成 ます ここにあるxml-to-dot.xsl

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xalan="http://xml.apache.org/xslt"
                exclude-result-prefixes="xalan">
    <xsl:output method="text"/>

    <xsl:template match="dag">
        digraph {
        rankdir="BT";
        node [style="filled", fillcolor="cyan", fontname="Helvetica"];
        <xsl:apply-templates select="//directed-edge-to"/>
        }
    </xsl:template>

    <xsl:template match="directed-edge-to">
        <xsl:value-of select="concat(ancestor::vertex/@name, '->', @vertex, ';')"/>
    </xsl:template>
</xsl:stylesheet>
4

2 に答える 2

8

=演算子で XPath の暗黙的な存在量化を利用できます。

<xsl:for-each select="//vertex[not(@name = //vertex/directed-edge-to/@vertex)]">

6 つの比較演算子 ( =!=<<=>および>=) のいずれかを使用してノード セットを比較すると、ノード セット内のいずれかのノードが条件を満たす場合、式は true を返します。あるノード セットを別のノード セットと比較する場合、最初のノード セットのいずれかのノードが 2 番目のノード セットのいずれかのノードと比較して条件を満たす場合、式は true を返します。XPath 2.0 では、この存在量化を実行しない 6 つの新しい演算子 ( eqneltlegtおよびge) が導入されています。しかし、あなたの場合、「=」を使用してその存在量化を取得したいと思うでしょう。

not()もちろん、これまでと同じように関数を使用したいことに注意してください。!=ほとんどの場合、オペレーターを避けるのは良いことです。ここで の代わりに使用すると、値と等しくない属性がnot()ある場合に true が返されますが、これは意図したものではありません。(また、いずれかのノード セットが空の場合、空のノード セットとの比較では常に false が返されるため、false が返されます。)@vertex@name

代わりに使用したい場合はeq、あなたがしたように何かをしなければなりません: 条件を反復から分離して、 bind できるようにしますcurrent()。しかし、XPath 2.0 では、式の中でこれを行うことができます。

<xsl:for-each select="for $v in //vertex
                      return $v[not(//directed-edge-to[@vertex eq $v/@name])]">

これは、条件が単純な等値比較ではない場合に役立ちます (そのため、" " を使用して存在を定量化することはできません=)。例: starts-with(@vertex, $v/@name).

XPath 2.0 には、存在量化を実行する明示的な方法もあります。上記の式の代わりに、次のforように書くこともできます。

<xsl:for-each select="//vertex[not(some $e in //directed-edge-to
                                   satisfies @name eq $e/@vertex)]">

" " 構文に加えてsome、XPath 2.0 は、全称量化を実行するための対応する " " 構文も提供ますevery

を使用する代わりfor-eachに、よりモジュール化された (そして強力な) テンプレート ルールを使用することもできます。

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <minimal-vertices>
      <xsl:apply-templates/>
    </minimal-vertices>
  </xsl:template>

  <!-- Copy vertex elements that have no arrows pointing to them -->
  <xsl:template match="vertex[not(@name = //directed-edge-to/@vertex)]">
    <minimal-vertex name="{@name}"/>
  </xsl:template>

</xsl:stylesheet>

繰り返しますが、この場合、 の存在量化に依存しています=

current()XSLT 1.0では、パターン、つまり属性での関数の使用が禁止されてmatchいますが、XSLT 2.0 では許可されています。その場合、current()現在一致しているノードを参照します。XSLT 2.0 では、(for式を使用せずに) 次のように書くこともできます。

<xsl:template match="vertex[not(//directed-edge-to[@vertex eq current()/@name])]">

このパターンは、 で使用しようとした式と本質的に同じですが、では必要なことfor-each行われませんが、パターンでは必要なことが行われることに注意してください (バインド先が異なるため)。for-eachcurrent()

最後に、いくつかの方法でロジックを簡素化する ( を削除する) バリエーションをもう 1 つ追加しますnot()。これも XSLT 1.0 の使用に戻ります。

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <minimal-vertices>
      <xsl:apply-templates/>
    </minimal-vertices>
  </xsl:template>

  <!-- By default, copy vertex elements -->
  <xsl:template match="vertex">
    <minimal-vertex name="{@name}"/>
  </xsl:template>

  <!-- But strip out vertices with incoming arrows -->
  <xsl:template match="vertex[@name = //directed-edge-to/@vertex]"/>

</xsl:stylesheet>

空白が出力されるのが気に入らない場合は、テキスト ノードに空のルールを追加して、それらが取り除かれるようにします (テキスト ノードのデフォルトのルールを上書きしてコピーします)。

<xsl:template match="text()"/>

または、テンプレートを適用するノードをより選択することもできます。

<xsl:apply-templates select="/dag/vertex"/>

どちらのアプローチを取るかは、部分的に好みに依存し、部分的にはスタイルシートと期待されるデータのより広いコンテキスト (入力構造がどの程度変化するかなど) に依存します。

私はあなたが求めていたものをはるかに超えたことを知っていますが、少なくともこれが興味深いと思っていただければ幸いです. :-)

于 2009-05-10T10:38:00.023 に答える
5

そのようなXPath1.0式の1つは次のとおりです。

        /*/vertex[not(@name = /*/vertex/directed-edge-to/@vertex)]

次に、それを次のようなXSLTスタイルシートに入れます

<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="/">
      <minimal-vertices>
          <xsl:for-each select=
          "/*/vertex[not(@name = /*/vertex/directed-edge-to/@vertex)]"
          >
           <minimal-vertex name="{@name}"/>
          </xsl:for-each>
      </minimal-vertices>
    </xsl:template>
</xsl:stylesheet>

このスタイルシートが最初に提供されたXMLドキュメントに適用される場合

<dag>
    <vertex name="A">
        <directed-edge-to vertex="C"/>
    </vertex>
    <vertex name="B">
        <directed-edge-to vertex="C"/>
        <directed-edge-to vertex="D"/>
    </vertex>
    <vertex name="C">
        <directed-edge-to vertex="E"/>
    </vertex>
    <vertex name="D">
        <directed-edge-to vertex="E"/>
    </vertex>
    <vertex name="E">
        <directed-edge-to vertex="G"/>
    </vertex>
    <vertex name="F">
        <directed-edge-to vertex="G"/>
    </vertex>
    <vertex name="G"/>
</dag>

必要な結果が生成されます:

<minimal-vertices>
  <minimal-vertex name="A" />
  <minimal-vertex name="B" />
  <minimal-vertex name="F" />
</minimal-vertices>

完全な(おそらく循環的な)グラフをトラバースするためのソリューションは、 ここのXSLTで利用できます。

于 2009-05-10T13:51:25.757 に答える