문제

a를 인코딩하는 XML 파일이 있습니다지시 된 acyclic 그래프 (DAG) 그것은 a를 나타냅니다 부분 순서. 이러한 그래프는 종속성 지정 및 찾기와 같은 것들에 유용합니다. 중요한 경로. 호기심을 위해, 내 현재 응용 프로그램은 빌드 시스템, 정점은 구성 요소이며 가장자리는 컴파일 타임 종속성을 지정합니다. 간단한 예는 다음과 같습니다.

<?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>

이 멍청이는 다음과 같이 그릴 수 있습니다.


(원천: iparelan.com)

신청하고 싶습니다 xslt 스타일 시트 이는 최소한의 요소 부분 순서의. 즉, 들어오는 가장자리가없는 정점. 예제 그래프의 최소 정점 세트는 {A, B, F}. 내 빌드 종속성 응용 프로그램의 경우이 세트를 찾는 것이이 세트의 구성원을 구축하면 프로젝트의 모든 것이 구축 될 것임을 알고 있기 때문에 가치가 있습니다.

다음은 내 현재 스타일 시트 솔루션입니다 (Apache Ant 's를 사용하여 Java에서 Xalan과 함께이 작업을 실행하고 있습니다. xslt 직무). 주요 관찰은 최소 정점이 어떤 것도 참조되지 않는다는 것입니다. 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>

문제는이 솔루션에 완전히 만족하지 않습니다. 결합 할 방법이 있는지 궁금합니다. selectfor-each 그리고 testif XPath 구문으로.

나는 다음과 같은 글을 쓰고 싶다 :

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

그러나 그것은 내가 원하는 것을하지 않습니다. current() 함수는 외부에서 선택한 노드를 참조하지 않습니다. //vertex 표현.

따라서 내 솔루션이 사용됩니다 xpath 1.0 그리고 XSLT 1.0 구문, 내가 개방적이지만 xpath 2.0 그리고 XSLT 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 언어 그래프 렌더링 코드. 여기에 있습니다 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>
도움이 되었습니까?

해결책

XPath의 암시 적 실존 정량화를 활용할 수 있습니다. = 운영자:

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

6 개의 비교 연산자 중 하나를 사용할 때 (=, !=, <, <=, >, 그리고 >=) 노드 세트를 비교하기 위해 노드 세트의 노드가 조건을 만족하면 표현식이 true를 반환합니다. 한 노드 세트를 다른 노드 세트와 비교할 때, 첫 번째 노드 세트의 노드가 두 번째 노드 세트의 노드와 비교할 때 조건을 충족하면 표현식이 true를 반환합니다. XPath 2.0은이 실존 적 정량화를 수행하지 않는 6 명의 새로운 연산자를 소개합니다 (eq, ne, lt, le, gt, 그리고 ge). 그러나 귀하의 경우 사용하고 싶을 것입니다. "="그 실존 적 정량화를 얻기 위해.

물론, 당신은 여전히 not() 당신이하고있는 것처럼 기능하십시오. 대부분의 경우, 피하는 것이 좋습니다. != 운영자. 대신 여기에서 사용했다면 not(), 그러면 @vertex 와 같지 않은 속성 @name 당신의 의도가 아닌 가치. (그리고 두 노드 세트 중 하나가 비어 있으면 빈 노드 세트와의 비교가 항상 False를 반환하기 때문에 False를 반환합니다.)

사용하려면 eq 대신, 당신은 당신이했던 것처럼 무엇을해야 할 것입니다 : 반복에서 조건부를 분리하여 묶을 수 있습니다. 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>

다시,이 경우, 우리는 실존 적 정량화에 의존하고 있습니다. =.

XSLT 1.0은 사용을 금지합니다 current() 패턴의 함수, 즉 match 속성이지만 xslt 2.0이 허용합니다. 이 경우 current() 현재 일치하는 노드를 나타냅니다. 따라서 XSLT 2.0에서는 이것을 쓸 수 있습니다 (사용하지 않고 for 표현):

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

이 패턴은 본질적으로 사용하려는 표현과 동일합니다. for-each, 그러나 그것은 당신이 원하는 것을하지 않는 반면 for-each, 그것 하다 패턴으로 원하는 것을하십시오 (왜냐하면 current() 구속력이 다릅니다).

마지막으로, 어떤면에서 논리를 단순화하는 한 가지 더 변형을 추가하겠습니다 (제거 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"/>

당신이 취하는 접근법은 부분적으로 취향에 따라 스타일 시트의 더 넓은 맥락과 예상 데이터 (입력 구조가 달라질 수있는 양 등)에 부분적으로 의존합니다.

나는 내가 당신이 요구하는 것 이상으로 나아 갔다는 것을 알고 있지만, 나는 당신이 적어도 이것을 흥미롭게 발견했기를 바랍니다. :-)

다른 팁

그러한 XPath 1.0 표현식 중 하나입니다:

        /*/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에서 사용할 수 있습니다. 여기.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top