Arbeiten auf Arrays mit Blockparametern
von Matthias Reitinger
am 27.04.08 um 20:43 (1619 Hits)
Voher
Der Tutorilaner Darian wollte im Thema „Array elemente bearbeiten...“ wissen, wie man mit PHP am elegantesten jedes Element eines Arrays bearbeitet. Dabei ergaben sich zwei unterschiedliche Lösungen (im Thema ging es eigentlich um eine etwas komplexere Aktion, die ich hier aber aus Gründen der Vereinfachung durch ein strtoupper() ersetzt habe):
Lösung A
Hier wird das Array „zu Fuß“ mit einer foreach-Schleife durchlaufen und jedes Element überschrieben.Code PHP:
1 2 3 4 5 6 7 <?php foreach ($array as $idx => $elm) { $array[$idx] = strtoupper($elm); } ?>
Lösung B
In dieser Variante (vorgeschlagen vom Benutzer Mairhofer) übernimmt array_walk() das Iterieren durch das Array. Für jedes Element wird die im zweiten Parameter angegebene Callback-Funktion aufgerufen, die sich um die Bearbeitung des Elements kümmert. Grundsätzlich ist das die elegantere Methode, die allerdings den Nachteil hat, dass man extra eine neue Funktion definieren muss. Dem kann man zwar durch create_function() aus dem Weg gehen – wirklich hübscher macht das den Quellcode aber auch nicht (eher im Gegenteil).Code PHP:
1 2 3 4 5 6 7 8 9 <?php function cb_strtoupper(&$elm, $key) { $elm = strtoupper($elm); } array_walk($array, 'cb_strtoupper'); ?>
Nachher
Code Ruby:
1 array.map! { |elm| elm.upcase }
Was passiert?
Block-Parameter
In Ruby kann jeder Methode ein sogenannter „Blockparameter“ mitgegeben werden. Dieser stellt ein Stück Code dar, das Parameter entgegen nimmt und einen Wert zurückgibt (praktisch wie eine Methode). Ein solcher Block wird entweder von geschweiften Klammern (wie im Quellcode) oder von do … end umschlossen. Die Parameterliste folgt direkt auf die öffnende Klammer (bzw. das do) und wird durch senkrechten Striche (|…|) begrenzt.
In der Methode kann dieser Codeblock dann beliebig oft mit verschiedenen Parametern aufgerufen und der Rückgabewert weiterverarbeitet werden. Genau das geschieht auch in der Methode map! der Array-Klasse: jedes Element wird dem Codeblock in einem Aufruf als Parameter übergeben und durch den Rückgabewert ersetzt.
Als Randnotiz sei noch erwähnt, dass der Rückgabewert eines Blocks (oder einer Methode) der Wert des letzten ausgewerteten Ausdrucks ist. Man muss also nicht explizit return wert schreiben (was aber auch möglich wäre), sondern es genügt wert.
Das Modul Enumerable
Neben map! gibt es noch eine Vielzahl anderer Array-Methoden, die mit Blockparametern arbeiten. Viele davon werden vom Mixin-Modul Enumerable bereitgestellt. Eine kleine Auswahl möchte ich an dieser Stelle kurz vorstellen:Das Schöne daran ist nun, dass diese Methoden genau auf dieselbe Weise verwendet werden können, solange die Klasse Enumerable einbindet. Beispielsweise könnte man so später ohne Probleme ein Array durch ein Objekt der Klasse Set (zur Repräsentation von Mengen) austauschen. In PHP wäre hier ein aufwändigeres Refactoring nötig (da foreach nur mit Arrays funktioniert).
- all?: Gibt true zurück, falls der Block für jedes Element true zurückgibt. Ansonsten false. Beispiel:
Code Ruby:
1 2 [1, 2, 3, 4].all? { |num| num < 5 } # => true [1, 2, 3, 4].all? { |num| num < 3 } # => false- select: Gibt ein Array der Elemente zurück, für die der Block true liefert. Beispiel:
Code Ruby:
1 [1, 2, 3, 4].select { |num| num < 3 } # => [1, 2]- partition: Unterteilt die Elemente in zwei Gruppen je nach (boole'schem) Rückgabewert des Blocks. Beispiel:
Code Ruby:
1 [1, 2, 3, 4].partition { |num| num < 3 } # => [[1, 2], [3, 4]]- sort_by: Sortiert die Elemente nach einer Eigenschaft, die der Block für jedes Element berechnet. Beispiel:
Code Ruby:
1 2 ["apple", "banana", "pear", "strawberry"].sort_by { |str| str.length } # => ["pear", "apple", "banana", "strawberry"]
Was haben wir gewonnen?
Durch das Konzept der Blockparameter kann in Ruby das Callback-Muster ohne Definieren zusätzlicher Methoden umgesetzt werden. Besonders beim Bearbeiten von Arrays kann man dadurch oft sehr kompakten und dennoch aussagekräftigen Quellcode schreiben. Außerdem ermöglicht das Modul Enumerable, häufig benötigte Operationen auf iterierbaren Datenstrukturen unabhängig von der konkreten Implementierung zu formulieren.
Desweiteren...
Blockparameter sind übrigens keine Erfindung von Ruby. Das darunter liegende Konzept existiert in funktionalen Programmiersprachen schon seit vielen Jahrzehnten als fundamentaler Bestandteil. Daher ist Ruby auch für Freunde funktionaler Programmierung durchaus einen Blick wert.
Über Kommentare zu diesem Beitrag würde ich mich wie immer freuen. Auch tiefergehende Fragen sind natürlich erlaubt und erwünscht.
- Matthias






