Thomas: Probleme mit for-each

Hallo an alle,

Ich habe folgendes Problem:
Ich werte alle Elemente einer .xml-Datei mit for-each aus. Nun verweisen einige Elemente auf ein weiteres Element. Immer wenn dieser Verweis vorhanden ist, möchte ich mir dann diese Elemente "merken" Nach dem Durchlaufen aller Elemente mit for-each möchte ich dann alle diese Elemente, die ich mir vorher "gemerkt" habe, weiter auswerten.

Ist dies so überhaupt möglich? Wenn ja, wie? Können dazu Variablen genutzt werden?

Der Grund ist, dass ich die Elemente ín eine bestimmte Reihenfolge bringen muss. Ein entsprechendes Sortierkriterium ist aber nicht vorhanden (xsl:sort nicht möglich). Die Sortierung kommt nur dadurch zu Stande, dass die einzelnen Elemente in einer best. Beziehung zueinander stehen. Einzig das erste Element ist vorgegeben.

Beispiel:

xml: (grob vereinfacht)

<Gruppe name="abc">
    <Element name="345" />
    <Element name="567" Gruppe="jkl" />
    <Element name="789" Gruppe="def" />
</Gruppe>
<Gruppe name="def">
    <Element name="369" />
    <Element name="258" />
    <Element name="147" />
</Gruppe>
<Gruppe name="ghi" top="yes">
    <Element name="123" Gruppe="mno" />
    <Element name="345" />
    <Element name="456" Gruppe="abc" />
</Gruppe>
<Gruppe name="jkl">
    <Element name="147" />
    <Element name="258" />
    <Element name="369" />
</Gruppe>
<Gruppe name="mno">
    <Element name="234" />
    <Element name="456" />
    <Element name="678" />
</Gruppe>

Ergebnis

ghi
  123
  345
  456

mno
  234
  456
  678

abc
  345
  567
  789

jkl
  147
  258
  369

def
  369
  258
  147

Vielen Dank schon mal für Eure Hilfe!

Gruß
Thomas

  1. Hallo,

    Ich habe folgendes Problem:
    Ich werte alle Elemente einer .xml-Datei mit for-each aus. Nun verweisen einige Elemente auf ein weiteres Element. Immer wenn dieser Verweis vorhanden ist, möchte ich mir dann diese Elemente "merken" Nach dem Durchlaufen aller Elemente mit for-each möchte ich dann alle diese Elemente, die ich mir vorher "gemerkt" habe, weiter auswerten.

    Ist dies so überhaupt möglich? Wenn ja, wie? Können dazu Variablen genutzt werden?

    Nein, in XSLT 1.0 ist das nicht möglich und mit for-each ist das auch in XSLT 2.0 nicht mögich, weil for-each keine Schleife ist.
    An sich sehe ich aber kein größeres Problem für eine Lösung, lediglich fehlt mir dabei --->

    Der Grund ist, dass ich die Elemente ín eine bestimmte Reihenfolge bringen muss. Ein entsprechendes Sortierkriterium ist aber nicht vorhanden (xsl:sort nicht möglich). Die Sortierung kommt nur dadurch zu Stande, dass die einzelnen Elemente in einer best. Beziehung zueinander stehen. Einzig das erste Element ist vorgegeben.

    Dann hast du doch ein Sortierkriterium.
    Dein Ergebnis zeigt mir aber auch nichts, was mit dem XSLT mittel nicht möglich wäre bzw. nichts von deinem Problem.
    --> Wenn das nicht das erwartete Ergebnis ist, wie sollte das denn aussehen?

    Grüße
    Thomas

    1. Hallo Thomas,

      for-each keine Schleife ist.

      Interessante Sichtweise, könntest du sie begründen. Damit es nicht langweilig wird, nehme ich schon mal den gegenteiligen Standpunkt ein, behaupte also, for-each ist sehr wohl eine Schleife :-)

      Gruß Uwe

      1. Hallo,

        for-each keine Schleife ist.

        Interessante Sichtweise, könntest du sie begründen. Damit es nicht langweilig wird, nehme ich schon mal den gegenteiligen Standpunkt ein, behaupte also, for-each ist sehr wohl eine Schleife :-)

        Ich könnte jetzt zwei Dinge sagen:

        1. *RTFA(rchiv)!*
        2. Begründe doch deinen Einwand. Du wirst doch Argumente dafür haben, warum es in einer for-each "schleift" und "loopt".

        Aber gut.
        Es gibt paar grundlegende Sachen, die man dazu wissen muss:

        1. die for-each Anweisung selektiert eine Kontenmenge und führt _dieselbe_ Verarbeitung für jeden der Konten durch.
          (so weit ist noch alles klar denke ich)

        2. for-each ist auch ein Template. Sie instanziiert seinen Inhalt (also das Template das sie enthält) _einmal für jeden selektierten Knoten_. Der aktuell Konten (current node) ist dabei immer derjenige der gerade abgearbeitet wird.

        3. In XSLT sind Variablen nicht update-fähig. D.h. der Wert einer Variable kann nicht zur Laufzeit verändert werden.

        4. XSLT ist eine funktionale Sprache, sie hat keine Seiteneffekte (so ein Seiteneffekt wäre Variablen zur Laufzeit zu ändern). Als funktionale Sprache arbeitet XSLT inkrementell (und das geht nur ohne Seiteneffekte), d.h. mehrere kleinere unabhängige "Funktionen", bei denen jeder ein Stück aus der Eingabe in Relation zu einer Stück aus der Ausgabe stellt. Deshalb gibt es in XSLT auch keine Zuweisungsangaben*

        Setzt man das zusammen ergibt sich folgendes Bild:
        In der konventionellen Programmierung werden Variablen oft dazu benutzt, um den Stand über den aktuellen "Ort" in der Schleife zu speichern.
        Bsp:

          
        iterator = liste.holeIterator();  
        while (iterator.hatMehrElemente()) {  
          elment = iterator.holeNaechtesElement();  
          elment.machEtwas();  
        }  
        
        

        * Die Zuweisungsangabe ist "elment = iterator.holeNaechtesElement();", diese Angabe weist jedes Mal der Variable "element" einen neuen Wert zu.

        So etwas in XSLT ist aber nicht möglich:

          
        <xsl:variable name="iterator" select="count(liste/*)" />  
        <xsl:for-each select="liste/*">  
         <xsl:if test="$iterator > 0">  
          .....  
          <xsl:variable name="iterator" select="$iterator - 1" />  
         </xsl:if>  
        </xsl:for-each>  
        
        

        Das funktioniert nicht.
        Auch andere Arten von Variable-Updates würde nicht funktionieren:

          
        <xsl:variable name="x" select="0" />  
        <xsl:for-each select="liste/*">  
         <xsl:value-of select="x + 1"/>  
        </xsl:for-each>  
        
        

        Die Ausgabe wäre immer "1", weil das Template in for-each jedes Mal neu für das aktuelle Element instantiiert wird und somit ist der Ausgangswert für "x" immer Null. Die nächste Iterationsrunde weiß nichts von der vorherigen.

        Daher: es schleift und loopt nichts im for-each.
        Will man eine "klassische Schleife" haben, greift man zur Rekursion.

        Grüße
        Thomas

        1. Hallo Thomas,

          danke für deine ausführliche Antwort.

          1. *RTFA(rchiv)!*

          Es würde mich natürlich interessieren, wer außer dir, diese Sichtweise noch unterstützt, falls du da Quellen hast – her damit, im Archiv finde ich nichts :-)

          1. Begründe doch deinen Einwand. Du wirst doch Argumente dafür haben, warum es in einer for-each "schleift" und "loopt".

          Absolut gerechtfertigt, werde ich gleich versuchen, aber du weißt ja, Alter vor Schönheit ;-)

          Es gibt paar grundlegende Sachen, die man dazu wissen muss:

          Du kannst davon ausgehen, dass ich mich mit XSLT auskenne, insbeondere auch das Funktionsmodell verstanden habe – hoffe ich zumindest ;-)

          1. die for-each Anweisung selektiert eine Kontenmenge

          Ja – klar.

          und führt _dieselbe_ Verarbeitung für jeden der Konten durch.

          Keineswegs, denk an Verzweigungen. Aber das war vermutlich nur schlecht formuliert.

          1. for-each ist auch ein Template.

          Nein, kurz und schmerzlos sage ich da – falsch. Passt auch gar nicht zu deinen nächsten Sätzen, ...

          Sie instanziiert seinen Inhalt (also das Template das sie enthält) _einmal für jeden selektierten Knoten_. Der aktuell Konten (current node) ist dabei immer derjenige der gerade abgearbeitet wird.

          ... die ich unterschreiben kann.

          1. In XSLT sind Variablen nicht update-fähig. D.h. der Wert einer Variable kann nicht zur Laufzeit verändert werden.

          Ja – klar.

          1. XSLT ist eine funktionale Sprache,

          Ich würde das gerne auf einer anschaulichen Ebene diskutieren, sonst reden wir plötzlich darüber wie XSLT beispielsweise als Sprache einzuordnen ist. Wir würden dann nicht weit kommen, fürchte ich :-)

          Jetzt folgen verschiedene Beispiele von dir.

          <xsl:variable name="x" select="0" />
          <xsl:for-each select="liste/*">
          <xsl:value-of select="x + 1"/>
          </xsl:for-each>

          Die Ausgabe wäre immer "1", weil das Template in for-each jedes Mal neu für das aktuelle Element instantiiert wird und somit ist der Ausgangswert für "x" immer Null.

          Deine Beispiele sind alle klar, wie gesagt, ich kenne mich aus.

          Die nächste Iterationsrunde weiß nichts von der vorherigen.

          Stimmt für dein Beispiel, ich möchte das aber allgemein nicht unterschreiben. Ich behaupte jetzt mal, die nächste Iterationsrunde, weiß sehr wohl etwas von der vorigen. Ich muss aber erst darüber nachdenken und reiche Begründung mit Beispiel nach.

          Daher: es schleift und loopt nichts im for-each.

          Alle deine Beispiele stützen sich nur auf das Argument, dass es in XSLT keine Variablen gibt. Hier will ich gerne einhaken und behaupten, dass Variablen keine Voraussetzung für Schleifen sind.

          Will man eine "klassische Schleife" haben, greift man zur Rekursion.

          Das will ich mal außen vor lassen, das ist eine gesonderte Frage, inwieweit man Rekursion mit einer Schleife vergleichen kann. Kann man, keine Frage, es geht nur darum inwieweit.

          Jetzt versuche ich mal meine Sichtweise darzustellen.
          Ob man for-each als Schleife gelten lassen will oder nicht, hängt letztendlich davon ab, was man als Schleife ansieht.
          Man kann grundsätzlich drei Arten von Schleifen unterscheiden, Zählerschleifen, Bedingungsschleifen und Mengenschleifen. Bei for-each handelt es sich um eine Mengenschleife.

          Ich habe eine geordnete Menge, hier eine Knotenliste, die current node list. Diese Menge (Knotenliste) wird in der gegebenen Reihenfolge durchlaufen, wobei bei jedem Durchlauf ein durch die Reihenfolge gegebenes Element (Knoten) zum gegenwärtigen Element (current node) wird, auf das ich unmittelbaren Zugriff habe. Ich mache doch nichts anderes, wie bei jeder Mengenschleife, als dass ich die Elemente einer Menge durchlaufe und die einzelnen Elemente dabei im gegebenen Rahmen nach Belieben manipulieren (hier bezüglich der Ausgabe) kann? Ist das keine Schleife? Ich sehe hier keinen grundsätzlichen Unterschied zu Mengenschleifen, wie sie in Programmiersprachen zur Anwendung kommen.

          Übrigens, um dich vielleicht etwas zu verwirren, auch »apply-templates« ist nach dieser Definition eine Schleife. Muss dann ja auch so sein, den prinzipiell gibt es ja zwischen »apply-templates« und »for-each« keinen Unterschied.

          Gruß Uwe

          1. Hallo,

            Du kannst davon ausgehen, dass ich mich mit XSLT auskenne, insbeondere auch das Funktionsmodell verstanden habe – hoffe ich zumindest ;-)

            Das ist fein, das macht das Gespräch interessanter ;-)
            Zuerst: in einem ganz betimmten Sinn hat du recht, man "kann" for-each als Schleife sehen, aber das muss zwangsläufig mit der Bedingung verknüpft sein, dass man sich _sehr_ genau mit dem Begriff "Schleife" und mit XSLT auskennt.
            Alles andere führt in erster Linie nur zu Verwirrung und zu Fragen wie: "wie kann ich in einer for-each Schleife etwas "merken"?".

            1. *RTFA(rchiv)!*
              Es würde mich natürlich interessieren, wer außer dir, diese Sichtweise noch unterstützt, falls du da Quellen hast – her damit, im Archiv finde ich nichts :-)

            Ich bin natürlich zutiefst enttäuscht, dass ich dir nicht genüge ;-), aber ich hoffe, dass du zu Namen wie Michael Kay, Jeni Tennison oder David Carlisle mehr vertrauen hast ;-)
            Nur ein Bsp: http://dpawson.co.uk/xsl/sect2/N5019.html
            (ich will jetzt nicht meine Mails aus der xsl-liste durchsuchen (sind über 90Tsd, ich muss mir da was einfallen lassen)).

            1. Begründe doch deinen Einwand. Du wirst doch Argumente dafür haben, warum es in einer for-each "schleift" und "loopt".
              Absolut gerechtfertigt, werde ich gleich versuchen, aber du weißt ja, Alter vor Schönheit ;-)

            Es gibt paar grundlegende Sachen, die man dazu wissen muss:

            und führt _dieselbe_ Verarbeitung für jeden der Konten durch.
            Keineswegs, denk an Verzweigungen. Aber das war vermutlich nur schlecht formuliert.

            Was meinst du genau mit Verzweigungen?
            Auch wenn du darunter Bedingungen innerhalb von for-each meinst, ändert das nicht daran, dass for-each dieselbe Anweisungen für jeden der selektierten Knoten ausführt. Natürlich, mag da die eine oder andere Bedingung sein, die dann für eine andre Ausgabe einzelner Knoten sorgt, dennoch: die Anweisungen werden für jeden Knoten vom Neun ausgeführt und der Kontestknoten dabei ggf. auf die Bedingung hin überprüft.

            1. for-each ist auch ein Template.
              Nein, kurz und schmerzlos sage ich da – falsch. Passt auch gar nicht zu deinen nächsten Sätzen, ...

            Sorry, das ist absolut korrekt. Das muss ich hier nochmal betonen.
            Ich habe das vielleicht nicht deutlich genung erklärt: der Inhalt von for-each ist als Template zu sehen. Dieses Template wird dann für den jeweiligen Knotexknoten instanziiert.
            Siehe auch: http://www.w3.org/TR/xslt#for-each. Dies hat sich im auch in XSLT 2 nicht geändert, dort wurde "Template" lediglich durch den Begriff "sequence constructor" ersetzt.
            ("The term sequence constructor  replaces template as used in XSLT 1.0. The change is made partly for clarity (to avoid confusion with template rules and named templates), ...")

            1. XSLT ist eine funktionale Sprache,
              Ich würde das gerne auf einer anschaulichen Ebene diskutieren, sonst reden wir plötzlich darüber wie XSLT beispielsweise als Sprache einzuordnen ist. Wir würden dann nicht weit kommen, fürchte ich :-)

            Das stimmt, es gibt viele mögliche Bezeichnungen für XSLT: deklarative, regelbasierte, funktionale etc. Spache.
            Aber so alleine stehe ich nicht mit der ansicht da:
            http://www.biglist.com/lists/xsl-list/archives/200308/msg01100.html
            (ich ließ die Suche in den Mails während des tippen doch laufen *g*)

            Die nächste Iterationsrunde weiß nichts von der vorherigen.
            Stimmt für dein Beispiel, ich möchte das aber allgemein nicht unterschreiben. Ich behaupte jetzt mal, die nächste Iterationsrunde, weiß sehr wohl etwas von der vorigen. Ich muss aber erst darüber nachdenken und reiche Begründung mit Beispiel nach.

            Das stimmt (bedingt). Will man die Position des aktuellen Knotens innerhalb der "current node list" ausgeben, geht das mit position(). Da könnte man schon annehmen, dass die Iterationsrunde "weiß" in wie wievielten Runde sie ist. Aber man kann nicht mit diesem Wissen über die Runden hinweg operieren: d.h. man kann diese Information nicht von einer Runde zur nächsten übergeben.

            Daher: es schleift und loopt nichts im for-each.
            Alle deine Beispiele stützen sich nur auf das Argument, dass es in XSLT keine Variablen gibt.

            Nein, das habe ich nicht gesagt! Was ich gesagt habe ist, dass es in XSLT keine "assignment statements" gibt. Das ist ein Unterschied!

            Hier will ich gerne einhaken und behaupten, dass Variablen keine Voraussetzung für Schleifen sind.

            Das muss du mir dann genauer erklären. ;-)
            (falls du das jetzt nicht speziell auf for-each bezogen hast, denn dabei wäre das in jedem Fall korrekt, dass dafür Variablen keine Voraussetzung sind.

            Will man eine "klassische Schleife" haben, greift man zur Rekursion.
            Das will ich mal außen vor lassen, das ist eine gesonderte Frage, inwieweit man Rekursion mit einer Schleife vergleichen kann. Kann man, keine Frage, es geht nur darum inwieweit.

            Da gebe ich dir Recht, dennoch bleibt einem, wenn man Variablen (korrekt in XSLT: Parameter) weiterreichen oder aus einer "Schleife" herausbrechen will, nichts anders übrig als auf Rekursion zurückzugreifen. Wohl nicht umsonst ist ein Prinzip für XSLT-Desing-Patterns das "don't iterate, recurse"  ;-)

            Jetzt versuche ich mal meine Sichtweise darzustellen.
            Ob man for-each als Schleife gelten lassen will oder nicht, hängt letztendlich davon ab, was man als Schleife ansieht.

            Genau.

            Man kann grundsätzlich drei Arten von Schleifen unterscheiden, Zählerschleifen, Bedingungsschleifen und Mengenschleifen. Bei for-each handelt es sich um eine Mengenschleife.

            Ich habe eine geordnete Menge, hier eine Knotenliste, die current node list. Diese Menge (Knotenliste) wird in der gegebenen Reihenfolge durchlaufen, wobei bei jedem Durchlauf ein durch die Reihenfolge gegebenes Element (Knoten) zum gegenwärtigen Element (current node) wird, auf das ich unmittelbaren Zugriff habe. Ich mache doch nichts anderes, wie bei jeder Mengenschleife, als dass ich die Elemente einer Menge durchlaufe und die einzelnen Elemente dabei im gegebenen Rahmen nach Belieben manipulieren (hier bezüglich der Ausgabe) kann?

            Korrekt.

            Ist das keine Schleife? Ich sehe hier keinen grundsätzlichen Unterschied zu Mengenschleifen, wie sie in Programmiersprachen zur Anwendung kommen.

            Wie du sagtest: es komt darauf an, was man unter Schleife versteht.

            Übrigens, um dich vielleicht etwas zu verwirren, auch »apply-templates« ist nach dieser Definition eine Schleife. Muss dann ja auch so sein, den prinzipiell gibt es ja zwischen »apply-templates« und »for-each« keinen Unterschied.

            *lach* dann verwirren uns jetzt gegenseitig, denn allgemein hast du zwar Recht, aber in einigen Details muss ich dir widersprechen.
            Ein prinzipieller Uterschied, der nichts direkt mit apply-templats und for-each zu tun hat, liegt im "Programmierstil" bzw. in der Verarbeitung duch den Prozessor.
            Die verwendung von apply-templates wird auch als "push processing" bezeichnet. Die 'lightweight' Version: der Prozessor verarbeitet ein Template und schickt es in die Welt hinaus und fragt "hat irgendein anderes Template an diesem Knoten Interesse?".
            Die Verwendung von for-each wird dagegen auch als "pull processing" bezeichnet, denn anstatt die Knoten in die Welt hinauszuschicken und von einem anderen Template verarbeiten zu lassen, holt man sie "herein" und behandelt sie selbst.

            Echte Unterschiede bestehen aber auch:
            <xsl:for-each select="a/*"> ist unterschiedlich als <apply-templates select="a/*" />, denn im Falle von for-each werden für jedes Element in <a> dieselben Anweisungen ausgeführt, bei apply-templates kann es aber durchaus für jedes Element in <a> unterschiedliche Templates exsitieren. Aber gut, das mag jetzt nur eine Kleinigkeit sein.

            Ein anderer Unterschied liegt dann vor, wenn es mehr als ein apply-templates , das auf einen selektieren Knoten zutrifft, gibt. Da spielt dann die Import-Präzedenz und die Priorität eine Rolle. Bei for-each spielt das keine Rolle, da werden die Anweisungen für den Knoten in jedem for-each ausgeführt.

            ...Hach die Uhrzeit! ... jetzt mache ich aber Schluß für heute!

            Grüße
            Thomas

            1. Hallo Thomas,

              Ich bin natürlich zutiefst enttäuscht, dass ich dir nicht genüge ;-), aber ich hoffe, dass du zu Namen wie Michael Kay, Jeni Tennison oder David Carlisle mehr vertrauen hast ;-)

              Keine Sorge, ich vertraue dir voll und ganz, ich bin nur um dich besorgt und möchte daher nicht, dass du womöglich einsam und verlassen, alleine auf weiter Flur stehst ;-) Aber ich ahne schon, ich scheine mich unnötig gesorgt zu haben :-)

              Nur ein Bsp: http://dpawson.co.uk/xsl/sect2/N5019.html

              Danke, werde ich mir aufmerksam durchlesen.

              der Inhalt von for-each ist als Template zu sehen. Dieses Template wird dann für den jeweiligen Knotexknoten instanziiert.

              Das ist klar, der Inhalt ist als Template zu sehen, aber nicht for-each selber. Also mich stört daran nur, dass du for-each selber als Template bezeichnet hast. Oder muss man for-each tatsächlich als Template sehen, jetzt bin ich gerade etwas verwirrt ;-)

              Das stimmt (bedingt). Will man die Position des aktuellen Knotens innerhalb der "current node list" ausgeben, geht das mit position(). Da könnte man schon annehmen, dass die Iterationsrunde "weiß" in wie wievielten Runde sie ist.

              Genau an position() habe ich gedacht. Dem Template sind ja zwei Dinge bekannt, der current node und die current node list und ich dachte ...
              "XSLT 1.0: The template is instantiated with the selected node as the current node, and with a list of all of the selected nodes as the current node list."

              Aber man kann nicht mit diesem Wissen über die Runden hinweg operieren: d.h. man kann diese Information nicht von einer Runde zur nächsten übergeben.

              ... man könnte an der current node list ansetzen. Aber ich fürchte du hast recht, sinnvoll geht das nicht.

              den prinzipiell gibt es ja zwischen »apply-templates« und »for-each« keinen Unterschied.
              *lach* dann verwirren uns jetzt gegenseitig, denn allgemein hast du zwar Recht, aber in einigen Details muss ich dir widersprechen.

              Ich meine tatsächlich nur prinzipiell, in den Details gibt es natürlich Unterschiede.

              Ein anderer Unterschied liegt dann vor, wenn es mehr als ein apply-templates , das auf einen selektieren Knoten zutrifft, gibt. Da spielt dann die Import-Präzedenz und die Priorität eine Rolle.

              Stimmt, daran habe ich nicht gedacht.

              Also Thomas, meine Sichtweise "for-each als Schleife" ist vermutlich wirklich zu naiv. Ich will nun, was ich schon Tim geschrieben habe, folgendes tun:
              Die Frage für mich wird vermutlich sein, ob ich ggf. wider besseren Wissens trotzdem noch von Schleife reden soll, denn auf "Anschaungsebene" tritt mir for-each als Schleife entgegen.
              Wenn es "praktisch" gesehen keinen Unterschied machen würde, ob ich von Schleife rede oder nicht, dann dürfte die Sichtweise "for-each als Schleife" keinerlei Auswirkung in Bezug auf die Herangehensweise an die konkrete Aufgabenstellung haben.
              Ich werde also versuchen ein Beispiel zu finden, wo diese Sichtweise tatsächlich Auswirkung hat und falls ich fündig werde, rede ich nie, nie mehr von Schleife :-)

              Der eigentliche Punkt ist aber, dank deiner und Tims Erklärungen fange ich langsam an zu ahnen, was eigentlich mit funktionaler Programmierung gemeint ist. Ich denke ich muss hier ansetzen (also doch theoretisch) um zu verstehen, warum man for-each nicht einfach so naiv als Schleife betrachten kann.

              Gruß Uwe

              1. Hi,

                Der eigentliche Punkt ist aber, dank deiner und Tims Erklärungen fange ich langsam an zu ahnen, was eigentlich mit funktionaler Programmierung gemeint ist. Ich denke ich muss hier ansetzen (also doch theoretisch) um zu verstehen, warum man for-each nicht einfach so naiv als Schleife betrachten kann.

                Ein weiterer Punkt:

                Eine Schleife kann (in den meisten Sprachen per break oder ähnlichem) abgebrochen werden. Alle weiteren Schleifendurchläufe finden nicht statt.

                In XSL gibt es kein <xsl:break /> oder vergleichbares.
                Die Ausführung aller Template-Instanzen von for-each unterliegt keiner bestimmten vorgeschriebenen Reihenfolge (nur die Ergebnisse müssen sortiert ausgegeben werden - entweder in document-Order oder wie durch xsl:sort explizit vorgegeben) - bei einem xsl:break/ wäre nicht vorhersagbar, welche Template-Instanzen schon verarbeitet wurden und welche noch fehlen ...

                cu,
                Andreas

                --
                Warum nennt sich Andreas hier MudGuard?
                O o ostern ...
                Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
                1. Hallo Andreas,

                  In XSL gibt es kein <xsl:break /> oder vergleichbares.

                  Ja, danke für den Hinweis.

                  Ich hatte mir bisher nie Gedanken gemacht, wie ich XSLT einordnen soll, so was ähnliches wie ein Programmiersprache, aber eben keine richtige ;-)
                  Jetzt wo ich anfange XSLT als funktionale Programmiersprache zu sehen, stellt sich da doch einiges anders dar. Ihr habt mich überzeugt, for-each ist keine Schleife.

                  Da gibt es nur noch ein klitzekleines Problemchen, was machen wir mit dem Rest der Welt ;-)
                  Denn wie ich schon sagte, auf "Anschauungsebene" tritt mir for-each durchaus als Schleife entgegen, vielleicht etwas eingeschränkt, aber grundsätzlich passt es durchaus. Nicht umsonst wird ganz überwiegend von Schleife und sogar von iterieren gesprochen. Ich beginne zu ahnen, wie sich Kopernikus gefühlt haben muss ;-)

                  Gruß Uwe

          2. Hallo Uwe,

            ich antworte mal in eine etwas andere Richtung in der Hoffnung, dass ein anderer Blick das klarer macht.

            1. XSLT ist eine funktionale Sprache,
              Ich würde das gerne auf einer anschaulichen Ebene diskutieren, sonst reden wir plötzlich darüber wie XSLT beispielsweise als Sprache einzuordnen ist.

            Zum Verständnis ist es aber durchaus praktisch, sich dessen bewusst zu sein. Dazu argumentiere ich mal ein bisschen durch die gedachten Vorteile, die man dadurch bekommt, dass XSLT funktional ist.

            Ich habe eine geordnete Menge, hier eine Knotenliste, die current node list. Diese Menge (Knotenliste) wird in der gegebenen Reihenfolge durchlaufen, wobei bei jedem Durchlauf ein durch die Reihenfolge gegebenes Element (Knoten) zum gegenwärtigen Element (current node) wird, auf das ich unmittelbaren Zugriff habe.

            Da stimmt Dir jeder zu – bis auf „in der gegebenen Reihenfolge durchlaufen“. Das ist nämlich nicht nötig.

            Stell Dir mal vor Du implementierst eine Funktion, die xsl:for-each in einem gedachtem XSLT-Prozessor implementiert. Dieser Prozessor übernimmt irgendwo das Parsen, startet irgendwo die Verarbeitung und ruft irgendwas xsl.forEach() mit bestimmten Parametern auf. Hier mal in Pseudo-JavaScript diese Funktion in einer naiven Variante:

            ~~~javascript function forEach (seq : Sequence, construct :  Function, sorting : Function) : Sequence {
                  var result : Sequence = new Sequence();

            for (currentNode in seq) {
                      var x : Sequence = construct(currentNode);
                      result.append(x);
                  }

            return result.sort(sorting);
              };

              
            Die Funktion nimmt als Parameter eine Sequenz (Das Resultat des select-Attributes), eine Sortier-Funktion (Meine gedachte Zusammenfassung von den optionalen <xsl:sort>-Elementen) und eine Funktion namens construct. Ich bleibe hier extra in der Terminologie von XSLT (2.0). Das Element <xsl:for-each> erwartet eine Sequenz auf der es arbeiten kann und innerhalb dürfen optionale <xsl-sort>s und ein Sequenz Konstruktor notiert werden. Letzterer gibt eine Sequenz von Knoten zurück, mehr wissen wir nicht über ihn. Deswegen ist er als abgeschlossene Funktion notiert. Die Funktion ist wie angekündigt billig und naiv, jeder Knoten in der Sequenz wird der Reihe nach durchlaufen, als Argument des Sequenz-Konstruktors aufgerufen, die zurückgegebene Sequenz wird an eine for-each-Sequenz (Array-ähnlich) eingehängt. Am Ende wird diese Sequenz mit dem Sortierkriterium (xsl:sort..) sortiert und zurückgegeben.  
              
            Da ist sie doch, die Schleife. Sogar eine Mengenschleife. Jupp. Ich sage ja, eine naive Implementierung. Machen wir es spannender. Was wird in letzer Zeit spannend an modernen Computern? U.a. dass die Prozessoren mehrere Kerne bekommen können. Mehrprozessor-Computer in einem Prozessor. Dadurch können Threads und Prozesse wirklich gleichzeitig ausgeführt werden und man kriegt einen Geschwindigkeitsschub. Das wäre doch toll, wenn forEach() das auch könnte. Dummerweise ist unsere Schleife iterativ und jeder Teilabschnitt der Schleife muss darauf warten, dass die vorherige Iteration beendet wurde. Also eine neue, immer noch naive Implementierung:  
              
              ~~~javascript
            function forEach (seq, construct, sorting) {  
                  var result = new Sequence();  
              
                  for (currentNode in seq) {  
                      var t = new Thread(function () {  
                                  var s = contruct(currentNode);  
                                  result.insert(currentNode.position, s);  
                              });  
                      t.run();  
                  };  
                  return result.sort(sorting);  
              };
            

            (Ich weiss, dass Javascript keine Threads kann. Also musste ich mir eine sehr simple API aus dem Arm schütteln, die keine Rücksicht auf Performance und ähnliches nimmt. Ist nur zu Demonstrationszwecken, nicht rumschreien.)

            Zwei Änderungen: Zum einen wird für jede Ausführung des Sequenz-Konstruktors ein neuer Thread erstellt, der angenommenerweise gerade auf dem Prozessor-(Kern) läuft, der gerade frei ist. Und zum anderen wird das Resultat nicht mehr einfach an die Resultatssequenz angehängt sondern an die „Position“ des Knotens gestellt. Dies liegt daran, dass wir uns nicht mehr sicher sein können, dass die Threads sich in Reihenfolge zurückmelden. Vielleicht braucht Iterationsschritt 19 einfach länger, wegen einer Verzweigung im Template oder weil es sich den Prozessor mit World of Warcraft teilen muss. Um die Reihenfolge zu wahren hab ich eine insert-Methode erfunden.

            Das ist der Knackpunkt. Das Starten der Ausführung der jeweiligen Verarbeitung eines Knotens geschieht hier immer noch in einer Schleife („Da loopt was“™). Aber das konkrete Ausführen und das Sammeln der Resultate nicht mehr. Das ist schon keine klassische Schleife mit einem vorhersehbaren Nacheinander mehr.

            Der andere Punkt sind die Variablen ausserhalb der Schleife. In einer klassischen iterativen Schleife kann man auf diese zugreifen, sie verändern und hat dennoch in jeden Iterationsschritt vorhersehbar und berechenbar weil es einen globalen konsistenten Status gibt. Stell Dir mal vor, construct() würde auf Variablen zugreifen, die ausserhalb ihrer selbst liegen. Und nun stell Dir vor:

            • Iterationsschritt 2 und 4 wollen gleichzeitig auf eine globale Variable zugreifen oder sie gar verändern.

            • Iterationsschritt 16 ist schneller als Iterationsschritt 4  und hat eine globale Variable verändert. Dummerweise braucht Iterationsschritt 4 noch den vorherigen Zustand – die Variable hat diesen aber nicht mehr.

            Es gibt haufenweise Methoden, um solche Konflikte zu verhindern, aber alle erfordern noch viel mehr Code und es macht das alles viel, viel komplexer. Und damit nerviger und bug-anfälliger. Und nun stell Dir vor, jede Funktion sei in sich abgeschlossen, kennt nur ihre Parameter und andere Funktionen und verändert nichts ausserhalb. Kleine, zweckbestimmte Arbeiter, die aus ihrer Eingabe eine Ausgabe produzieren und sonst nichts machen. Und komplexere Funktionen sind ebenso kleine Funktionen, die aber mit anderen Funktionen arbeiten.

            Das ist meiner Meinung nach einer der Hauptvorteile an Funktionalen Programmiersprachen und mit ein Grund, weswegen die in sehr nerdigen Zirkeln plötzlich wieder hip werden: Die sind sehr einfach zu parallelisieren. Und das ist nicht nur hypothetisch.

            Stell Dir mal vor, Du wärst verrückt und hättest das gesamte spiderbare Internet in XHTML normalisiert und in einer einzigen XML-Datei gespeichert. Und nun wolltest Du ein neues XML-Dokument erstellen, dass die Adressen aller Webseiten enthält, die auf http://de.selfhtml.org/ verlinken. Und das in XSLT; ich sag ja: verrückt. ;)

            ~~~xml output:sites
                  <xsl:for-each select="/g:internet/g:page">
                      <xsl:if test="//xhtml:a[@href = 'http://de.selfhtml.org/']">
                          output:site
                              <xsl:attribute name="uri">
                                  <xsl:value-of select="@href"/>
                              </xsl:attribute>
                          </output:site>
                      </xsl:if>
                  </xsl:for-each>
              </output:sites>

              
            Du wärst doch sehr froh, wenn da xsl:for-each einfach parallelisierbar wäre statt einer Schleife, so dass die konkrete Ausführung verteilt laufen könnte. Zum Beispiel auf dem größtem Computercluster der Welt.  
              
            Google hat sich ein eigenes Software-Framework geschrieben, dass mit solch funktionalen Methoden solch riesige Berechnungen erstellt wie z.B. den Pagerank zu berechnen. Das Problem wird in kleine, unabhängig voneinander arbeitende Funktionen aufgeteilt und diese mit dann mit einer Funktion ähnlich die for-each parallelisiert. Ähnlich, obwohl dort natürlich noch viel, viel mehr passiert, die Jobs werden auf haufenweise Computer aufgeteilt und es gibt Vorkehrungen dafür, dass da ein Computerknoten abstürzen kann und und und. Aber das Prinzip ist ähnlich. Der Name des Frameworks lautet [MapReduce](http://labs.google.com/papers/mapreduce.html).  
              
            [Map()](http://en.wikipedia.org/wiki/Map_%28higher-order_function%29) und [Reduce()](http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29) sind zwei Archetypen von generellen Funktionen die auf Kollektionen (Listen, Arrays arbeiten) und dazu eine weitere beliebige Funktion nutzen. Wenn man xsl:sort weglässt, ist xsl:for-each nichts anderes als map() – es führt eine Funktion auf jedes Element einer Sequenz aus und gibt die andere Sequenz zurück.  
              
              `var result = map(seq, construct);`{:.language-javascript} (1)  
              
            Es ist \_keine\_ Schleife, weil das wesentliche Element einer Schleife fehlt: Das Nacheinanderfolgen der Ausführung der Funktion. Es ist schlichtweg wurst, in welcher Reihenfolge das geschieht – schließlich kann die Funktion die Welt ausserhalb sich selbst nicht verändern.  
              
            (1) In echtem JS 1.6: `var result = seq.map(construct)`{:.language-javascript}  
              
              
              
            
            > Übrigens, um dich vielleicht etwas zu verwirren, auch »apply-templates« ist nach dieser Definition eine Schleife. Muss dann ja auch so sein, den prinzipiell gibt es ja zwischen »apply-templates« und »for-each« keinen Unterschied.  
              
            Ja. Oder apply-templates ist eine Funktion, die auf gegebene Knoten jeweils passende Funktionen (Templates) anwendet. Soll heissen: Ich sehe inzwischen in jedem XSLT-Konstrukt eine Funktion, weil es ich es nur noch als funktionelle Sprache sehe.  
              
            Aber auch wenn ich hier massiv von Parallelisierung rede und stark denke, dass sich die Autoren von XSLT-Prozessoren das auf jeden Fall nutzen, glaube ich nicht, dass das der wesentliche Grund hinter der Erstellung von XSLT/XPath als Funktionale Programmiersprache war. Ich glaube eher, dass das funktionale Programmierparadigma sehr gut auf die Baumstruktur der Eingabe und die Baumstruktur Ausgabe passte. Eine Funktion für den gesamten Baum, die Funktionen auf Teilbäume anwenden, die Funktionen auf Teilbäume anwenden, die Funktionen auf Knoten anwenden. Das ist ein weiterer Teilaspekt der Funktionalen Programmierung, durch die Abschottung und Spezialisierung von Funktionen in der Zusammenarbeit mit generellen Funktion kann man sich immer gut auf Teilaspekte des Problems konzentrieren und kann dadurch einer Vermischung von Top (Dem Großen Ganzen) und Bottom (dem fitzeligen Detail bei der Hand) aus dem Weg gehen und dadurch saubereren Programmcode erhalten. Wobei „sauber“ bei der verfluchten XML-Syntax eher konzeptionell zu begreifen ist. Ist aber nur meine Privattheorie, vielleicht waren die XSLT-Erfinder auch nur Sprachfanatiker. Aber dieser Aspekt der Aufteilung von Problemen in lauter kleinere unabhängige Probleme als passender Programmierstil für XML-Bäume war hier nicht so gut zu erklären wie das Beispiel der Parallelisierung, deswegen musstest Du unter diesem Sermon leiden.  
              
              
            Tim
            
            1. Hallo Gunnar,

              Ein paar Anmerkungen:

              result.insert(currentNode.position, s);
              Um die Reihenfolge zu wahren hab ich eine insert-Methode erfunden.

              Die Kontext-Position ist tatsächlich eine der Informationen die beim Evalulieren des Sequenz Konstruktors laut XSLT 2.0 vorhanden sein dürfen.

              Das ist meiner Meinung nach einer der Hauptvorteile an Funktionalen Programmiersprachen und mit ein Grund, weswegen die in sehr nerdigen Zirkeln plötzlich wieder hip werden

              Zirkeln wie programming reddit, wo sich seit einem Jahr oder gefühlt ein Drittel der Postings mit Funktionalen Sprachen wie Haskell beschäftigen.

              Stell Dir mal vor, Du wärst verrückt und hättest das gesamte spiderbare Internet in XHTML normalisiert und in einer einzigen XML-Datei gespeichert. Und nun wolltest Du ein neues XML-Dokument erstellen, dass die Adressen aller Webseiten enthält, die auf http://de.selfhtml.org/ verlinken.

              Mit Entschuldigung an Joel Spolsky.

              (1) In echtem JS 1.6: var result = seq.map(construct)

              Dass nur in Mozilla oder unter Zuhilfename eines Frameworkes funktioniert, sorry.

              Tim

            2. Hallo Tim,

              ich antworte mal in eine etwas andere Richtung in der Hoffnung, dass ein anderer Blick das klarer macht.

              Ja, du hast mir mit diesem anderen Blick sehr geholfen.

              Ich habe eine geordnete Menge, hier eine Knotenliste, die current node list. Diese Menge (Knotenliste) wird in der gegebenen Reihenfolge durchlaufen, wobei bei jedem Durchlauf ein durch die Reihenfolge gegebenes Element (Knoten) zum gegenwärtigen Element (current node) wird, auf das ich unmittelbaren Zugriff habe.
              Da stimmt Dir jeder zu – bis auf „in der gegebenen Reihenfolge durchlaufen“. Das ist nämlich nicht nötig.

              Du hast recht, ich habe mich da unglücklich ausgedrückt, das mit xsl:sort ist schon klar.

              Das ist der Knackpunkt. Das Starten der Ausführung der jeweiligen Verarbeitung eines Knotens geschieht hier immer noch in einer Schleife („Da loopt was“™). Aber das konkrete Ausführen und das Sammeln der Resultate nicht mehr. Das ist schon keine klassische Schleife mit einem vorhersehbaren Nacheinander mehr.

              Ja, sehr schön erklärt, du bist auf dem besten Wege mich zu überzeugen :-)

              Es ist _keine_ Schleife, weil das wesentliche Element einer Schleife fehlt: Das Nacheinanderfolgen der Ausführung der Funktion. Es ist schlichtweg wurst, in welcher Reihenfolge das geschieht – schließlich kann die Funktion die Welt ausserhalb sich selbst nicht verändern.

              Ich muss darüber nachdenken, aber vermutlich hast du mich schon überzeugt :-)
              Die Frage für mich wird vermutlich sein, ob ich ggf. wider besseren Wissens trotzdem noch von Schleife reden soll, denn auf "Anschaungsebene" tritt mir for-each als Schleife entgegen.
              Wenn es "praktisch" gesehen keinen Unterschied machen würde, ob ich von Schleife rede oder nicht, dann dürfte die Sichtweise "for-each als Schleife" keinerlei Auswirkung in Bezug auf die Herangehensweise an die konkrete Aufgabenstellung haben.
              Ich werde also versuchen ein Beispiel zu finden, wo diese Sichtweise tatsächlich Auswirkung hat und falls ich fündig werde, rede ich nie, nie mehr von Schleife :-)

              Aber dieser Aspekt der Aufteilung von Problemen in lauter kleinere unabhängige Probleme als passender Programmierstil für XML-Bäume war hier nicht so gut zu erklären wie das Beispiel der Parallelisierung, deswegen musstest Du unter diesem Sermon leiden.

              Das Beispiel mit der Parallelisierung war sehr anschaulich und hast mir damit die Idee der funktionalen Programmierung ein gutes Stück näher gebracht.

              Gruß Uwe

      2. Hallo Uwe,

        Interessante Sichtweise, könntest du sie begründen. Damit es nicht langweilig wird, nehme ich schon mal den gegenteiligen Standpunkt ein, behaupte also, for-each ist sehr wohl eine Schleife :-)

        Stelle Dir einen Baumstamm mit 10 Ästen vor, welche nacheinander oder je nach Tool (Kettensäge bzw. XSLT-Prozessor) auch gleichzeitig abgetrennt (abgearbeitet) werden. Das ist keine Schleife ("weil da loopt nix" [TM]), sondern eine Mehrfachverarbeitung.

        Erst XSLT/XPath 2.0 bieten einen Ansatz von Schleifen und auch das sind eigentlich keine, sondern Sequenzkonstruktoren wie a to b oder for-in-return. Im Zusammenspiel mit xsl:for-each sieht das dann schon sehr schleifenmäßig aus, obwohl auch hier nichts weiter als die mehrfache Abarbeitung der Sequenz aus atomaren Werten von 1 bis 10 erfolgt:

        <xsl:for-each select="(1 to 10)">  
          <xsl:value-of select="."/><br />  
        </xsl:for-each>
        

        Alternativ auch so formulierbar:

        <xsl:value-of select="(1 to 10)" separator="&lt;br /&gt;" disable-output-escaping="yes"/>

        Hier noch der Auszug der geraden Zahlen über ein for-in-return-Konstrukt:

        <xsl:for-each select="for $i in (1 to 10) return if($i mod 2 eq 0) then $i else()">  
          <xsl:value-of select="."/><br />  
        </xsl:for-each>
        

        Grüße,
        Thomas

        1. Hallo Thomas,

          sorry, dass ich nicht geantwortet habe, mir ist erst gerade aufgefallen, dass ich es hier ja mit ganz verschiedenen Thomasen zu tun habe :-)

          Gruß Uwe

    2. Hallo Thomas,

      Dann hast du doch ein Sortierkriterium.
      Dein Ergebnis zeigt mir aber auch nichts, was mit dem XSLT mittel nicht möglich wäre bzw. nichts von deinem Problem.
      --> Wenn das nicht das erwartete Ergebnis ist, wie sollte das denn aussehen?

      Ich versteh' nicht ganz wie dies denn möglich sein soll. Ich durchlaufe alle Gruppen mit for-each und sortiere zuerst nach dem Attribut top. Damit habe ich gleich die erste Gruppe "ghi". So weit so gut. Nun weiß ich aber nicht, wie ich nun sortieren soll, um als nächstes die Gruppe "mno" auszuweisen. Der Hinweis dazu steht erst in der Gruppe "ghi".

      Kannst Du hier vielleicht einen Tipp geben? Ich bin leider noch Anfänger in XSL.

      Beispiel:

      xml: (grob vereinfacht)

      <Gruppe name="abc">
          <Element name="345" />
          <Element name="567" Gruppe="jkl" />
          <Element name="789" Gruppe="def" />
      </Gruppe>
      <Gruppe name="def">
          <Element name="369" />
          <Element name="258" />
          <Element name="147" />
      </Gruppe>
      <Gruppe name="ghi" top="yes">
          <Element name="123" Gruppe="mno" />
          <Element name="345" />
          <Element name="456" Gruppe="abc" />
      </Gruppe>
      <Gruppe name="jkl">
          <Element name="147" />
          <Element name="258" />
          <Element name="369" />
      </Gruppe>
      <Gruppe name="mno">
          <Element name="234" />
          <Element name="456" />
          <Element name="678" />
      </Gruppe>

      Ergebnis

      ghi
        123
        345
        456

      mno
        234
        456
        678

      abc
        345
        567
        789

      jkl
        147
        258
        369

      def
        369
        258
        147

      Gruß
      Thomas

      1. Nur noch als kleine Anmerkung:

        folgendermaßen habe ich es schon hinbekommen.

        ghi
          123
            mno
              234
              456
              678
          345
          456
            abc
              345
              567
                jkl
                  147
                  258
                  369
              789
                def
                  369
                  258
                  147

        Allerdings ohne for-each, erst die Elemente habe ich mit for-each ausgewertet und habe dann das Template des Gruppen-Knotens erneut aufgerufen. Da nun alle Gruppen ineinander verschachtelt sind, hilft mir dies leider nicht so richtig weiter. Daher kam mir dann die Idee, die einzelnen Gruppen-Attribute zu merken, um die entsprechenden Gruppen separat aufzurufen.

        Weiß hier jemand Rat, wie dies mit XSL zu lösen ist?

        Gruß
        Thomas

        1. Hallo,

          Sorry, ich habe wirklihc übersehen, dass du noch geantwortet hast!

          Ich versteh' nicht ganz wie dies denn möglich sein soll. Ich durchlaufe alle Gruppen mit for-each und sortiere zuerst nach dem Attribut top. Damit habe ich gleich die erste Gruppe "ghi". So weit so gut. Nun weiß ich aber nicht, wie ich nun sortieren soll, um als nächstes die Gruppe "mno" auszuweisen. Der Hinweis dazu steht erst in der Gruppe "ghi".

          OK, jetzt verstehe ich das Problem.

          Hier eine Lösung mit lauten for-each. Sie würde für dein Beispiel funktionieren, aber auch nur genau dafür, d.h. nur für eine Hierarchie von "top --> untergruppe --> unter-unter-gruppe".

            
          <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">  
           <xsl:key name="topG" match="Gruppe[@top]" use="."/>  
           <xsl:key name="secG" match="Gruppe" use="@name"/>  
           <xsl:template match="/data">  
            
              <xsl:for-each select="key('topG', Gruppe)">  
               <!--  
                hier was auch immer die ausgabe sein soll  
                <xsl:value-of select="@name"/>  
               -->  
               <xsl:for-each select="Element">  
                <!--  
                 hier was auch immer die ausgabe sein soll  
                 <xsl:value-of select="@name"/>  
                -->  
               </xsl:for-each>  
               <!-- hier das Ende der Gruppe!!!  also z.B.: </div> setzen -->  
               <xsl:for-each select="key('secG', Element/@Gruppe)">  
                <xsl:sort select="//Element[@Gruppe = current()/@name]/@name" data-type="number" />  
                <!--  
                 hier was auch immer die ausgabe sein soll  
                 <xsl:value-of select="@name"/>  
                -->  
                <xsl:for-each select="Element">  
                 <!--  
                  hier was auch immer die ausgabe sein soll  
                  <xsl:value-of select="@name"/>  
                 -->  
                </xsl:for-each>  
                <!-- hier das Ende der Gruppe!!!  also z.B.: </div> setzen -->  
                <xsl:for-each select="key('secG', Element/@Gruppe)">  
                 <xsl:sort select="//Element[@Gruppe = current()/@name]/@name" data-type="number" />  
                 <!--  
                  hier was auch immer die ausgabe sein soll  
                  <xsl:value-of select="@name"/>  
                 -->  
                 <xsl:for-each select="Element">  
                  <!--  
                   hier was auch immer die ausgabe sein soll  
                   <xsl:value-of select="@name"/>  
                  -->  
                 </xsl:for-each>  
                 <!-- hier das Ende der Gruppe!!!  also z.B.: </div> setzen -->  
                </xsl:for-each>  
               </xsl:for-each>  
              </xsl:for-each>  
           </xsl:template>  
          
          

          Die ausgabe ist:
          ghi
           123
           345
           456
          mno
           234
           456
           678
          abc
           345
           567
           789
          jkl
           147
           258
           369
          def
           369
           258
           147

          Hier eine andere Lösung, wo die Hierarchietiefe keine Rolle spielt:

            
          <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">  
           <xsl:key name="topG" match="Gruppe[@top]" use="."/>  
           <xsl:key name="secG" match="Gruppe" use="@name"/>  
           <xsl:template match="/data">  
          ...  
              <xsl:for-each select="key('topG', Gruppe)">  
               <xsl:value-of select="@name"/>  
               <xsl:apply-templates select="Element">  
                <xsl:sort select="@name" data-type="number"/>  
               </xsl:apply-templates>  
               <xsl:apply-templates select="key('secG', Element/@Gruppe)">  
                <xsl:sort select="//Element[@Gruppe = current()/@name]/@name" data-type="number"/>  
               </xsl:apply-templates>  
              </xsl:for-each>  
           </xsl:template>  
           <xsl:template match="Gruppe">  
            <xsl:value-of select="@name"/>  
            <xsl:apply-templates select="Element">  
             <xsl:sort select="@name" data-type="number"/>  
            </xsl:apply-templates>  
            <xsl:apply-templates select="key('secG', Element/@Gruppe)">  
             <xsl:sort select="//Element[@Gruppe = current()/@name]/@name" data-type="number"/>  
            </xsl:apply-templates>  
           </xsl:template>  
           <xsl:template match="Element">  
            <xsl:value-of select="@name"/>  
           </xsl:template>  
          ...  
          
          

          Grüße
          Thomas

          1. Hallo Thomas,

            Hier eine andere Lösung, wo die Hierarchietiefe keine Rolle spielt:

            da ich nie weiß, wie tief die Hierarchie wirklich geht, ist dies wohl der richtigere Ansatz.

            <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
            <xsl:key name="topG" match="Gruppe[@top]" use="."/>
            <xsl:key name="secG" match="Gruppe" use="@name"/>
            <xsl:template match="/data">
            ...
                <xsl:for-each select="key('topG', Gruppe)">
            ...
                 <xsl:apply-templates select="key('secG', Element/@Gruppe)">
            ...

              
            Ich werde mir das Ganze mal anschauen und versuchen nachzuvollziehen. (Leider ist mein xml-File etwas umfangreicher und das xsl:key-Element ist mir noch etwas fremd.)  
              
            Aber vielen Dank erst mal für diesen Ansatz.  
            Ggf. werde ich mich wieder melden.  
              
            Gruß  
            Thomas
            
          2. Hallo Thomas,

            Hier eine andere Lösung, wo die Hierarchietiefe keine Rolle spielt:

            vielen Dank für die Antwort, ich habe es erst einmal grob so umsetzen können. Die Lösung ist wirklich hervorragend.

            Allerdings habe ich noch ein kleines Problem:

            Bei mir erscheinen direkt vor der ersten Gruppenbezeichnung 3 Leerzeichen. Und direkt nach der der Abarbeitung erscheinen auch sehr viele Leerzeichen der Form:

            ...
            ......
            .........
            ............
            ............
            ............
            .........
            ......
            ......
            .........
            ............
            ............
            ............
            .........
            ......
            ......
            .........
            ............
            ............
            ............
            .........
            ......
            ......
            .........
            ............
            ............
            ............
            .........
            ......
            ......
            .........
            ............
            ............
            ............
            .........
            ......
            ......

            usw.

            Hast Du eine Idee, wo diese herkommen könnten? Eigentlich habe ich Dein  Script genau so umgesetzt und kann keinen Fehler finden. Sonst funzt auch alles!

            Ach so: als Ergebnis möchte ich kein HTML erhalten, sondern eine reine Textdatei. Deshalb habe ich auch noch die Zeile

            <xsl:output method="text" />

            eingefügt.

            Gruß
            Thomas

            1. Hallo,

              Hier eine andere Lösung, wo die Hierarchietiefe keine Rolle spielt:

              vielen Dank für die Antwort, ich habe es erst einmal grob so umsetzen können. Die Lösung ist wirklich hervorragend.

              Allerdings habe ich noch ein kleines Problem:

              Bei mir erscheinen direkt vor der ersten Gruppenbezeichnung 3 Leerzeichen. Und direkt nach der der Abarbeitung erscheinen auch sehr viele Leerzeichen der Form:

              Ach so: als Ergebnis möchte ich kein HTML erhalten, sondern eine reine Textdatei. Deshalb habe ich auch noch die Zeile

              <xsl:output method="text" />

              Hast Du eine Idee, wo diese herkommen könnten?

              Du darst dann keine Einrückungen im XML/XSL haben, die werden dann als Leerzeichen ausgegeben. Versuche es mit: http://de.selfhtml.org/xml/darstellung/xsltelemente.htm#strip_space

              Grüße
              Thomas

              1. Hallo Thomas,

                Du darst dann keine Einrückungen im XML/XSL haben, die werden dann als Leerzeichen ausgegeben.

                Damit habe ich die Leerzeichen vor der Ausgabe herausbekommen, aber die am Ende sind geblieben.
                Ich habe aber eine andere Lösung gefunden:
                Die Zeile <xsl:for-each select="key('topG', Gruppe)"> habe ich gestrichen und die oberste Ebene direkt mit Gruppe[@top='yes'] aufgerufen. Damit funzt es.

                Aber danke noch mal. Auf die Lösung der Auswertung der einzelnen Hierarchieebenen wäre ich nie gekommen.

                Gruß
                Thomas

  2. Hallo,

    hat hier keiner eine Lösung parat oder einen kleinen Tipp, wie man es lösen könnte? Ich brauche dringend einen Rat.

    Gruß
    Thomas

    1. Hallo,

      hat hier keiner eine Lösung parat oder einen kleinen Tipp, wie man es lösen könnte? Ich brauche dringend einen Rat.

      https://forum.selfhtml.org/?t=155673&m=1014472

      Grüße
      Thomas