Weiß jemand, was die schlimmste mögliche asymptotische Verlangsamung ist, die auftreten kann, wenn rein funktional und nicht zwingend programmiert wird (dh Nebenwirkungen zugelassen werden)?
Klarstellung aus dem Kommentar von itowlson : Gibt es ein Problem, bei dem der bekannteste zerstörungsfreie Algorithmus asymptotisch schlechter ist als der bekannteste destruktive Algorithmus, und wenn ja, um wie viel?
Antworten:
Laut Pippenger [1996] kann beim Vergleich eines rein funktionalen (und nicht faulen) Lisp-Systems mit einem System, das Daten mutieren kann, ein Algorithmus übersetzt werden, der für das unreine Lisp geschrieben wurde, das in O ( n ) ausgeführt wird zu einem Algorithmus in der reinen Lisp, der in O ( n log n ) -Zeit ausgeführt wird (basierend auf Arbeiten von Ben-Amram und Galil [1992] zur Simulation des Direktzugriffsspeichers mit nur Zeigern). Pippenger stellt auch fest, dass es Algorithmen gibt, für die dies das Beste ist, was Sie tun können. Es gibt Probleme, die im unreinen System O ( n ) sind und im reinen System Ω ( n log n ) sind.
Bei diesem Papier sind einige Einschränkungen zu beachten. Das wichtigste ist, dass es sich nicht um faule funktionale Sprachen wie Haskell handelt. Bird, Jones und De Moor [1997] zeigen, dass das von Pippenger konstruierte Problem in O ( n ) Zeit in einer faulen funktionalen Sprache gelöst werden kann , aber sie stellen nicht fest (und soweit ich weiß, hat niemand), ob oder Keine faule funktionale Sprache kann alle Probleme in derselben asymptotischen Laufzeit lösen wie eine Sprache mit Mutation.
Das von Pippenger konstruierte Problem erfordert , dass der in dem linearen Problem gesehene Ω ( n log n ) speziell konstruiert ist, um dieses Ergebnis zu erzielen, und ist nicht unbedingt repräsentativ für praktische Probleme in der realen Welt. Es gibt einige Einschränkungen für das Problem, die etwas unerwartet sind, aber erforderlich sind, damit der Beweis funktioniert. Das Problem erfordert insbesondere, dass die Ergebnisse online berechnet werden, ohne auf zukünftige Eingaben zugreifen zu können, und dass die Eingabe aus einer Folge von Atomen aus einer unbegrenzten Menge möglicher Atome und nicht aus einer Menge fester Größe besteht. Und das Papier legt nur (untere Grenze) Ergebnisse für einen unreinen Algorithmus der linearen Laufzeit fest; Bei Problemen, die eine längere Laufzeit erfordern, ist es möglich, dass das zusätzliche O (log n ) -Faktor bei zusätzlichen Operationen, die für Algorithmen mit längeren Laufzeiten erforderlich sind, möglicherweise "absorbiert" werden kann. Diese Klarstellungen und offenen Fragen werden von Ben-Amram [1996] kurz untersucht .
In der Praxis können viele Algorithmen in einer reinen Funktionssprache mit der gleichen Effizienz wie in einer Sprache mit veränderlichen Datenstrukturen implementiert werden. Eine gute Referenz zu Techniken zur effizienten Implementierung rein funktionaler Datenstrukturen finden Sie in Chris Okasakis "Rein funktionale Datenstrukturen" [Okasaki 1998] (eine erweiterte Version seiner Arbeit [Okasaki 1996] ).
Jeder, der Algorithmen für rein funktionale Datenstrukturen implementieren muss, sollte Okasaki lesen. Sie können im schlimmsten Fall immer eine O (log n ) -Verzögerung pro Operation erzielen, indem Sie veränderlichen Speicher mit einem ausgeglichenen Binärbaum simulieren. In vielen Fällen können Sie jedoch erheblich bessere Ergebnisse erzielen, und Okasaki beschreibt viele nützliche Techniken, von amortisierten Techniken bis hin zu realen. Zeit diejenigen, die die amortisierten Arbeit schrittweise erledigen. Rein funktionale Datenstrukturen können etwas schwierig zu bearbeiten und zu analysieren sein, bieten jedoch viele Vorteile wie referenzielle Transparenz, die bei der Compileroptimierung, beim parallelen und verteilten Rechnen sowie bei der Implementierung von Funktionen wie Versionierung, Rückgängigmachen und Rollback hilfreich sind.
Beachten Sie auch, dass in all dem nur asymptotische Laufzeiten behandelt werden. Viele Techniken zur Implementierung rein funktionaler Datenstrukturen führen zu einer gewissen konstanten Verlangsamung des Faktors, da zusätzliche Buchhaltung erforderlich ist, damit sie funktionieren, und Implementierungsdetails der betreffenden Sprache. Die Vorteile rein funktionaler Datenstrukturen können diese konstanten Faktorverlangsamungen überwiegen. Daher müssen Sie im Allgemeinen Kompromisse eingehen, die auf dem betreffenden Problem basieren.
Verweise
quelle
Es gibt in der Tat mehrere Algorithmen und Datenstrukturen, für die selbst bei Faulheit keine asymptotisch effiziente rein funktionale Lösung (eine, die in der reinen Lambda-Rechnung implementierbar ist) bekannt ist.
Wir gehen jedoch davon aus, dass in "imperativen" Sprachen der Zugriff auf den Speicher O (1) ist, während dies theoretisch nicht so asymptotisch sein kann (dh für unbegrenzte Problemgrößen) und der Zugriff auf den Speicher innerhalb eines riesigen Datensatzes immer O (log n) ist. , die in einer funktionalen Sprache emuliert werden können.
Wir müssen uns auch daran erinnern, dass tatsächlich alle modernen Funktionssprachen veränderbare Daten liefern, und Haskell liefert sie sogar, ohne die Reinheit zu beeinträchtigen (die ST-Monade).
quelle
In diesem Artikel wird behauptet, dass die bekannten rein funktionalen Implementierungen des Union-Find-Algorithmus alle eine schlechtere asymptotische Komplexität aufweisen als die von ihnen veröffentlichten, die eine rein funktionale Schnittstelle haben, aber intern veränderbare Daten verwenden.
Die Tatsache, dass andere Antworten behaupten, dass es niemals einen Unterschied geben kann und dass der einzige "Nachteil" von rein funktionalem Code beispielsweise darin besteht, dass er parallelisiert werden kann, gibt Ihnen eine Vorstellung von der Informiertheit / Objektivität der funktionalen Programmiergemeinschaft in diesen Fragen .
BEARBEITEN:
Die folgenden Kommentare weisen darauf hin, dass eine voreingenommene Diskussion der Vor- und Nachteile einer reinen funktionalen Programmierung möglicherweise nicht von der „Community der funktionalen Programmierung“ stammt. Guter Punkt. Vielleicht sind die Befürworter, die ich sehe, nur, um einen Kommentar zu zitieren, "Analphabeten".
Ich denke zum Beispiel, dass dieser Blog-Beitrag von jemandem geschrieben wurde, von dem man sagen könnte, dass er repräsentativ für die funktionale Programmier-Community ist, und da es sich um eine Liste von „Punkten für eine verzögerte Bewertung“ handelt, wäre es ein guter Ort, um einen Nachteil zu erwähnen faule und rein funktionale Programmierung könnte haben. Ein guter Ort wäre anstelle der folgenden (technisch zutreffenden, aber voreingenommenen Entlassung) gewesen:
quelle
Bei einer festen Obergrenze für die Speichernutzung sollte es keinen Unterschied geben.
Beweisskizze: Bei einer festgelegten Obergrenze für die Speichernutzung sollte es möglich sein, eine virtuelle Maschine zu schreiben, die einen imperativen Befehlssatz mit derselben asymptotischen Komplexität ausführt, als ob Sie tatsächlich auf dieser Maschine ausgeführt würden. Dies liegt daran, dass Sie den veränderlichen Speicher als persistente Datenstruktur verwalten können, wobei O (log (n)) gelesen und geschrieben wird. Mit einer festen Obergrenze für die Speichernutzung können Sie jedoch eine feste Speichermenge haben, wodurch diese entstehen Zerfall zu O (1). Daher kann die funktionale Implementierung die zwingende Version sein, die in der funktionalen Implementierung der VM ausgeführt wird, und daher sollten beide dieselbe asymptotische Komplexität aufweisen.
quelle
Ich würde vorschlagen, über die Leistung von Haskell zu lesen und dann einen Blick auf die Benchmark-Spielleistungen für funktionale Sprachen im Vergleich zu prozeduralen / OO-Sprachen zu werfen .
quelle