Einen String n-mal duplizieren

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie müssen einen String n-mal duplizieren, wobei n ein Parameter ist. Zum Beispiel müssen Sie einen String mit Leerzeichen auffüllen, um eine bestimmte Ausrichtung zu erreichen.

Lösung

XSLT 1.0

Eine schöne Lösung ist ein rekursiver Ansatz, der den Eingabestring so lange verdoppelt, bis er die erforderliche Länge erreicht hat, wobei sorgfältig Fälle behandelt werden, in denen $count ungerade ist:

<xsl:template name="dup">
  <xsl:param name="input"/>
  <xsl:param name="count" select="2"/>
  <xsl:choose>
    <xsl:when test="not($count) or not($input)"/>
    <xsl:when test="$count = 1">
      <xsl:value-of select="$input"/>
    </xsl:when>
    <xsl:otherwise>
      <!-- Wenn $count ungerade ist, wird eine zusätzliche Kopie der Eingabe angehängt -->
      <xsl:if test="$count mod 2">
        <xsl:value-of select="$input"/>
      </xsl:if>
      <!-- Rekursives Anwenden des Templates nach dem Verdoppeln der Eingabe und dem Halbieren des Zählers -->
      <xsl:call-template name="dup">
        <xsl:with-param name="input" select="concat($input,$input)"/>
        <xsl:with-param name="count" select="floor($count div 2)"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

XSLT 2.0

In 2.0 können wir das Duplizieren ziemlich einfach mit einem for-Ausdruck erledigen. Wir überladen dup, um das Verhalten des vorgegebenen Arguments in der XSLT 1.0-Implementierung zu replizieren:

<xsl:function name="ckbk:dup">
  <xsl:param name="input" as="xs:string"/>
  <xsl:sequence select="ckbk:dup($input,2)"/>
</xsl:function>

<xsl:function name="ckbk:dup">
  <xsl:param name="input" as="xs:string"/>
  <xsl:param name="count" as="xs:integer"/>
  <xsl:sequence select="string-join(for $i in 1 to $count return $input,'')"/>
</xsl:function>

Diskussion

XSLT 1.0

Die offensichtlichste Methode, um einen String $count-mal zu duplizieren, besteht darin, eine Möglichkeit zu ermitteln, den String $count-1-mal mit sich selbst zu verketten. Dies kann mit Hilfe des folgenden Codes rekursiv erledigt werden, allerdings ist dieser Code für größere $count-Werte sehr aufwändig und wird deshalb nicht empfohlen:

<xsl:template name="slow-dup">
  <xsl:param name="input"/>
  <xsl:param name="count" select="1"/>
  <xsl:param name="work" select="$input"/>
  <xsl:choose>
    <xsl:when test="not($count) or not($input)"/>
    <xsl:when test="$count=1">
      <xsl:value-of select="$work"/><
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="slow-dup">
        <xsl:with-param name="input" select="$input"/>
        <xsl:with-param name="count" select="$count - 1"/>
        <xsl:with-param name="work" select="concat($work,$input)"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Ein besserer Ansatz wird in der Lösung oben gezeigt. Die Lösung beschränkt die Anzahl der rekursiven Aufrufe und Verkettungen auf die Größenordnung log2($count), indem wiederholt die Eingabe verdoppelt und der Zähler halbiert werden, solange der Zähler größer als 1 ist. Die slow-dup-Implementierung ist heikel, da sie einen künstlichen Arbeitsparameter erfordert, um die ursprüngliche Eingabe zu beobachten. Sie kann außerdem aufgrund der Rekursion von $count-1 ein Stack-Wachstum zur Folge haben und erfordert $count-1-Aufrufe von concat(). Vergleichen Sie das mit dup, das das Stack-Wachstum auf floor(log2($count)) beschränkt und nur ceiling(log2($count))-Aufrufe von concat() verlangt.

Anmerkung:
Zugunsten der slow-dup-Technik lässt sich anbringen, dass sie auch verwendet wird, um zusätzlich zu den Strings auch die Struktur zu duplizieren, wenn wir xsl:value-of durch xsl:copy-of ersetzen. Das schnellere dup besitzt in diesem Fall keinen Vorteil, da die Kopien als Parameter übergeben werden, was aufwändig ist.

Eine andere Lösung, die auf dem Code des EXSLT-str:padding beruht, damit aber nicht identisch ist, sieht so aus:

<xsl:template name="dup">
  <xsl:param name="input"/>
  <xsl:param name="count" select="1"/>
  <xsl:choose>
    <xsl:when test="not($count) or not($input)" />
    <xsl:otherwise>
      <xsl:variable name="string" select="concat($input, $input, $input, $input, $input, $input, $input, $input, $input, $input)"/>
      <xsl:choose>
        <xsl:when test="string-length($string) &gt;= $count * string-length($input)">
          <xsl:value-of select="substring($string, 1, $count * string-length($input))" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="dup">
            <xsl:with-param name="input" select="$string" />
            <xsl:with-param name="count" select="$count div 10" />
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Diese Implementierung legt zehn Kopien der Eingabe an. Wenn dieser Ansatz mehr erreicht, als erforderlich ist, stutzt er das Ergebnis auf die gewünschte Größe. Ansonsten wendet er das Template rekursiv an. Diese Lösung ist langsamer, da sie oft mehr Verkettungen vornimmt, als notwendig sind, und sie verwendet substring(), das bei manchen XSLT-Implementierungen langsam sein kann. Im Rezept Text ersetzen finden Sie eine Erklärung. Die Lösung erweist sich für solche Prozessoren als vorteilhaft, die die Endrekursion nicht optimieren, da sie die Anzahl der rekursiven Aufrufe deutlich verringert.

Siehe auch

Die sogenannte Piez-Methode kann einen String ebenfalls ohne Rekursion duplizieren. Diese Methode wird im Artikel XSLT – Efficient Programming Techniques (PDF) besprochen. Sie verwendet eine for-each-Schleife auf jeder verfügbaren Quelle von Knoten (oft auf dem Stylesheet selbst). Obwohl diese Methode in der Praxis außerordentlich effektiv sein kann, finde ich sie unzulänglich, da sie davon ausgeht, dass genügend Knoten zur Verfügung stehen, um die erforderliche Iteration auszuführen.

  

<< zurückvor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de