Ankündigung

Einklappen
Keine Ankündigung bisher.

3. Weihnachtlicher Dauerlauf

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • 3. Weihnachtlicher Dauerlauf

    ASCII Iterator
    1) Was sind Iteratoren?

    Iteratoren sind ein in PHP 5.0 eingebautes ein Feature in PHP, mit welchem man innerhalb einer Schleife ein Objekt durchlaufen kann, was gerade bei sehr großen Listen Performancevorteile im Vergleich zu Arrays bringt.

    Es gibt in PHP zwei Interfaces für Iteratoren:

    - Iterator
    - IteratorAggregate
    - Fertig Iterator Implementierungen in PHP

    Iterator


    Das Iterator Interface besitzt 5 Methoden:

    - rewind()
    - valid()
    - next()
    - key()
    - current()


    Die rewind(), valid() und next() Methoden lassen sich anhand einer for()-Schleife erklären:

    PHP-Code:
    for ($i 0$i 10$i++) {} 
    $i = 0 setzt den Iterator auf 0 - genau wie rewind().

    $i < 10; prüft, ob die Iterationsrunde durchgeführt wurde. Wenn $i größer 10 ist, dann wird die Runde nicht mehr durchgeführt - genau diese Prüfung führt valid().

    $i++ setzt $i um eins hoch, bevor die nächste Iterationsrunde durchgeführt wird - genau wie next()

    Im Prinzip könnte man also einen Iterator manuell via for()-Schleife iterieren:
    PHP-Code:
    for ($iterator->rewind(); $iterator->valid(); $iterator->next() {} 
    current() gibt den aktuellen Wert zurück und key() den Schlüssel.

    Hier mal ein kleines Beispiel:

    PHP-Code:
    <?php
    class ExampleIterator implements Iterator {
      private 
    $mockValues = ['foo''bar'];
      private 
    $row 0;

      public function 
    rewind() {
        
    $this->row 0;
      }
      public function 
    valid() {
        return 
    $this->row <= count($this->mockValues);
      }
      public function 
    next() {
        
    $this->row++;
      }
      public function 
    key() {
        return 
    $this->row;
      }
      public function 
    current() {
        return 
    $this->mockValues[$this->row];
      }
    }

    foreach (new 
    ExampleIterator as $key => $value) {
      echo 
    $key ' => ' $value PHP_EOL;
    }
    Die Ausgabe sähe dann etwa so aus:

    Code:
    0 => foo
    1 => bar
    Allerdings ist für Anwendungsfälle in einem derartigem Größenbereich ein Iterator der totale Overkill - da würden auch normale arrays ausreichen.

    IteratorAggregate

    Das Iterator Interface eignet sich hervorragend, wenn während der Iteration die eigentlichen Werte erst errechnet werden müssen, wie zum Beispiel bei dem unten gezeigtem ASCII Weihnachtsbaum Iterator. Wenn man aber nur sehr große Wertelisten hat, dann eignet sich IteratorAggregate besser, da man einfach einen ArrayIterator returnen kann. Dieses Interface besitzt eine Methode:

    - getIterator()

    Diese Methode returned einen Iterator mit den Werten, über die dann iteriert werden kann.

    iterator_to_array()


    Iteratoren haben aber im Vergleich zu Arrays einen entscheidenden Nachteil: man kann die array_* Funktionen nicht nutzen, da diese auf Arrays einen Type Hint setzen, aber nicht auf ein Iterator oder IteratorAggregate interface. Falls man also an einer Code Stelle die array Funktionen braucht, aber einen Iterator hat, müsste man jetzt über einen Iterator drüberiterieren und diesen in einen Array schreiben. Zum Glück stellt PHP aber die Funktion iterator_to_array für solche Probleme zur Verfügung.

    Diese Methode erwartet ein Traversable Interface. Sowohl Iterator, als auch IteratorAggregate erben von diesem Interface und folglich können alle Klassen, die eines der beiden Interfaces besitzen, bei IteratorAggregate den Rückgabewert von getIterator() oder das Resultat von Iterator, zu einem Array konvertiert.

    yield (Generatoren)

    Seit PHP 5.5 hat PHP ein neues Feature, die Generatoren, eingeführt. Diese tun im Prinzip das gleiche, wie das Iterator Interface, bloß muss man nicht eine ganze Klasse implementieren, sondern nur yield in der Funktion nutzen. Hier ein Beispiel mit yield, das die gleiche Ausgabe wie im obigen Beispiel erziehlt:
    PHP-Code:
    <?php
    function generator()
    {
        
    $mockValues = ['foo''bar'];
        foreach (
    $mockValues as $value) {
            yield 
    $value;
        }
    }
    foreach (
    generator() as $key => $value) {
        echo 
    $key ' => ' $value PHP_EOL;
    }
    Wie man erkennen kann, tut yield im Prinzip nichts anderes, irgendwelche Werte in den Generator zu packen. Dadurch wird die Funktion zu einem Generator, über welchen man iterieren kann, obwohl es keinen Rückgabewert gibt.

    2) ASCII Weihnachtsbaum


    Nach dieser Einführung für Iteratoren hier mal ein Beispiel, bei dem mit Hilfe eines Iterators ein Weihnachtsbaum generiert wird. beispielhafte Ausgabe:

    Code:
        X
       XXX
      XXXXX
     XXXXXXX
    XXXXXXXXX
        X
       XXX
      XXXXX
     XXXXXXX
    XXXXXXXXX
        X
       XXX
      XXXXX
     XXXXXXX
    XXXXXXXXX
       XXX
       XXX
    Und hier der Code:

    PHP-Code:
    <?php
    class ASCIIChristmasTreeIterator implements Iterator
    {
        private 
    $row;
        private 
    $char;
        private 
    $pineConeLength;
        private 
    $pineConeCount;
        private 
    $rootHeight;
        public function 
    __construct($char$pineConeCount 3$pineConeLength 9$rootHeight 2)
        {
            if (
    $pineConeLength === 0) {
                throw new 
    InvalidArgumentException('Pine cone length must be an odd number');
            }

            
    $this->pineConeLength = (integer) $pineConeLength;
            
    $this->pineConeCount  = (integer) $pineConeCount;
            
    $this->rootHeight     = (integer) $rootHeight;
            
    $this->char           = (string) $char;
        }

        public function 
    rewind()
        {
            
    $this->row 0;
        }

        public function 
    valid()
        {
            
    $middle = ($this->pineConeLength 2) * $this->pineConeCount $this->rootHeight;
            return 
    $this->row <= floor($middle) || $this->row <= ceil($middle);
        }

        public function 
    next()
        {
            
    $this->row++;
        }

        public function 
    key()
        {
            return 
    $this->row;
        }

        public function 
    current()
        {
            static 
    $height 1;
            static 
    $count  1;

            if (
    $height $this->pineConeLength) {
                
    $height 1;
                
    $count++;
            }

            if (
    $count $this->pineConeCount) {
                return [
                    
    'indent'  => $this->calculateIndent(3),
                    
    'content' => str_repeat($this->char3)
                ];
            }

            
    $data = [
                
    'content' => str_repeat($this->char$height),
                
    'indent'  => $this->calculateIndent($height)
            ];
            
    $height $height 2;

            return 
    $data;
        }

        private function 
    calculateIndent($content)
        {
            return (
    $this->pineConeLength $content) / 2;
        }
    }

    foreach (new 
    ASCIIChristmasTreeIterator('X') as $pineCone) {
        echo 
    str_repeat(' '$pineCone['indent']) . $pineCone['content'] . PHP_EOL;
    }

  • #2
    Danke für den Artikel. *like*

    Sollte man generell auf Iteratoren setzen oder in welchen Situationen ist das nicht sinnvoll?


    Gruß Rodney

    Kommentar


    • #3
      Es kommt auf die Situation an und auf die Strutkur dessen was du iterieren willst. Iteratoren können für die Verschiedensten Dinge ( Aggregation, Traversion, Endlosschleifen, ... ) genutzt werden, sind aber nicht in jeder Situation sinnvoll.
      [URL="https://gitter.im/php-de/chat?utm_source=share-link&utm_medium=link&utm_campaign=share-link"]PHP.de Gitter.im Chat[/URL] - [URL="https://raindrop.io/user/32178"]Meine öffentlichen Bookmarks[/URL] ← Ich habe dir geholfen ? [B][URL="https://www.amazon.de/gp/wishlist/348FHGUZWTNL0"]Beschenk mich[/URL][/B].

      Kommentar


      • #4
        Kleiner Fehler...
        PHP-Code:
          public function valid() { 
            return 
        $this->row <= count($this->mockValues); 
          } 
          public function 
        current() { 
            return 
        $this->mockValues[$this->row]; 
          } 
        Wenn $this->row einen Index in ein normalisiertes (0-basiertes) Array darstellt, liegt ein gültiger Index zwischen 0 und count-1 (daher ist die Abfrage in valid() auf "kleiner-gleich" falsch).
        Über 90% aller Gewaltverbrechen passieren innerhalb von 24 Stunden nach dem Konsum von Brot.

        Kommentar

        Lädt...
        X