1unitedpower: Gibt es einen Sinn für Anweisungen in if-Bedingiungen?

Beitrag lesen

Gut, das entschärft den Punkt mit den Magic Strings, aber die Verständlichkeit finde ich dadurch auch nicht großartig erhöht, wenn man da nun eine Variable hat, die da weiterhin den Funktionsaufruf versteckt.

Ich will gar nicht leugnen, dass meine Argumentation für PHP nicht so gut aufgeht wie für JavaScript. Funktionale Programmierung ist in JavaScript wesentlich prominenter als in PHP und im Umkehrschluss bietet PHP weniger Komfort für funktionale Programmierung - da beißt sich die Katze auch selber in den Schwanz. Zum Beispiel gibt es in PHP keinen asynchronen Event-Loop, also gibt es auch keinen Bedarf an asynchronen Datenstrukturen wie Streams oder Promises.

Ich will trotzdem nochmal Wort für die funktionale Lösung erfreifen. Wir sind bis jetzt davon ausgegangen, dass wir nur ein Array durchlaufen. Dazu reicht eine einzige überschaubare Schleife. Das Iterieren wird allerdings umso schwieriger, je verzweigter die Datenstrukturen sind. Nehmen wir zum Beispiel einen Baum: Da reicht eine Schleife nicht mehr aus, und wie mans auch letztlich macht, man sieht dem Code auf einem flüchtigen Blick nicht mehr an, dass er der Iterations eines Baumes dient. Schlimmer wird es, wenn man den Baum mehrmals Iterieren möchte und immer wieder den gleichen Iterations-Algorithmus niederschreiben muss. Niemand will schließlich Code-Duplikation. Man lagert die Iterations-Logik also in eigenen Code aus. Da gibt es nun viele Möglichkeiten das zu tun, eine viel gesehene Variante ist das Iterator-Interface zu implementieren. Dann ließe sich der Baum wieder mit einer Schleife traversieren.

foreach($tree as $node) {
   $processed_nodes[] = process_node[$node];
}

Das sieht zunächst vielversprechend aus. Aber die Einfachheit ist trügerisch. Der Anwendungsfall den wir bisher betrachtet haben, hat die Iteration eines Arrays erfordert und uns am Ende ein Array gleicher Länge geliefert. Oder wir hatten einen Stream, den wir iteriert haben und hatten am Ende einen neuen Stream gleicher Länge. Nun wollen wir den Baum iterieren und am Ende einen Baum haben, der die selbe Struktur wie der vorherige aufweist: Also die gleiche Anzahl an Knoten, aber vor allem auch die gleichen Verzweigungen an korrespondierenden Knoten. Viel Glück mit dem Iterator-Interface, Stichworte RecursiveTreeIterator und RecursiveIteratorIterator.

Das beschriebene Iterationsschema ist omnipräsent: Man hat einen Container-Typen, will mit jedem Wert darin eine Berechnung durchführen und am Ende den gleiche Container-Typen wieder haben. Das ist genau das, was die map-Funktion macht: Und zwar für Listen, Arrays, Streams, Promises (hier häufig then genannt), Bäume, Graphen etc… Und der Code ist immer sehr ähnlich und einfach:

$processedArray = array_map($processItem, $array);
$processedList = $list->map($processItem);
$processedTree = $tree->map($processItem);
// etc. pp

Viel einfacher geht es eigentlich nicht mehr. Und da enden die Vorteile noch nicht: map ist nur eins von vielen wiederkehrenden Iterations-Schemen. Es gibt zich weitere: reduce, filter, zip, group… In den vollen Genuss kommt man natürlich erst, wenn man eine Library (z.B. pramda) einsetzt, die einem die diversen Algorithmen über konsistente Schnittstellen zugänglich macht.