Stellen Sie sich eine funktionale Programmiersprache vor, deren einzige Datentypen numerische Skalare und beliebige Verschachtelungen von Arrays sind. Der Sprache fehlen alle Mittel zur unbegrenzten Iteration, daher sind die folgenden nicht zulässig:
- explizite Schleifen (ohne Nebenwirkungen sowieso nicht viel zu gebrauchen)
- Rekursion
- beliebige erstklassige Funktionen (kein y-Kombinator)
Die Sprache hat jedoch:
- Funktionen der obersten Ebene
- lexikalisch angelegt lassen Bindungen
- Verzweigungssteuerungsfluss
- gemeinsame skalare mathematische und logische Funktionen
- Ein einfacher Array-Konstruktor wie fill (n, x), der ein n-Element-Array mit identischen Werten x erstellt
- Am wichtigsten ist: eine eingeschränkte Gruppe von Operatoren höherer Ordnung, die eine parallel strukturierte Iteration durchführen (z. B. Map, Reduce, Scan, All-Pair).
Genauer gesagt zu den Datenparalleloperatoren:
- y = Karte (f, x) => y [i] = f [i]
- y = reduziere (f, a, x) => y = f (a, f (y [p [0]], f (y [p [1]], ...))) für eine Permutation p
- y = Scan (f, a, x) => y [i] = reduzieren (f, a, y [0 ... i-1])
- y = alle Paare (f, x, y) => y [i, j] = f (x [i], y [j])
Wir könnten auch andere Operatoren haben, aber um sie zu qualifizieren, sollten sie eine Polynomlaufzeit haben, unter einem vernünftigen Modell der datenparallelen Berechnung implementierbar sein und höchstens Polynomraum verwenden.
Es gibt offensichtlich einige Konstrukte, die in dieser Sprache nicht ausgedrückt werden können, wie zum Beispiel:
while f(x) > tol:
x <- update(x)
Was können wir in diesem System ausdrücken? Beschränken wir uns nur auf Suchprobleme in FP? Können wir alle polynomiellen Zeitalgorithmen erfassen? Gibt es auch eine minimale Anzahl von Operatoren für diese Klasse?
quelle
Meine andere Antwort wies auf Mängel in der aktuellen Sprache hin. In dieser Antwort werde ich detaillierter auf die Probleme mit der Koexistenz von Falten und Entfaltungen in einer Gesamtsprache eingehen. Dann werde ich eine Lösung vorschlagen und zeigen, dass alle Probleme in P (und in der Tat viele weitere) in dieser erweiterten Sprache gelöst werden können.
Das Falten in einer Gesamtsprache verbraucht Listen:
Das Entfalten in einer Gesamtsprache erzeugt Streams, die möglicherweise unbegrenzt sind:
Leider leben Listen und Streams in verschiedenen Welten, sodass diese Operatoren nicht zusammengestellt werden können. Wir brauchen eine teilweise Korrespondenz:
Der Stream-Operator bettet eine Liste in einen begrenzten Stream ein. Die Listenfunktion extrahiert die ersten n Elemente (oder weniger, wenn der Stream früher beendet wird) in eine Liste. Wir haben also die folgende Gleichung:
Als Effizienzoptimierung können wir Streams als Zwischendatenstruktur vollständig ausschneiden:
Ich werde nun einen Beweis skizzieren, dass dies (mit den anderen Operatoren, die bereits in der ursprünglichen Frage impliziert sind) die Simulation eines beliebigen Polynom-Zeit-Algorithmus ermöglicht.
Per Definition ist eine Sprache L in P, wenn es eine Turing-Maschine M und ein Polynom p gibt, so dass die Zugehörigkeit von x in L durch Ausführen von M höchstens p (n) Iterationen mit n = | x | entschieden werden kann. Durch ein Standardargument kann der Zustand des Bandes der Maschine in Iteration k mit einer Liste von höchstens 2k + 1 codiert werden, obwohl das Band der Maschine unendlich ist.
Die Idee ist nun, Ms Übergang als Funktion von Listen zu Listen darzustellen. Die Ausführung der Maschine erfolgt durch Entfalten des Ausgangszustandes mit der Übergangsfunktion. Dies erzeugt einen Strom von Listen. Die Annahme, dass L in P ist, bedeutet, dass wir nicht weiter als p (n) Elemente in den Strom schauen müssen. So können wir die Entfaltung mit komponieren
list p(n)
, um eine endliche Liste zu erhalten. Schließlich falten wir es um, um zu prüfen, ob die Antwort auf das Entscheidungsproblem Ja oder Nein war.Allgemeiner zeigt dies, dass wir einen Algorithmus simulieren können, dessen Abschlusszeit durch eine in der Sprache berechenbare Funktion begrenzt werden kann. Dies legt auch nahe, warum so etwas wie Ackermanns Funktion nicht simuliert werden kann: Es ist eine eigene Bindung, daher gibt es ein Henne-Ei-Problem.
quelle
Es ist eine sehr gimped Sprache. Versuchen Sie, die Fakultätsfunktion zu programmieren:
Das Problem ist, dass Ihre Sprache nur Falten hat, aber keine Falten. Die natürliche Art, Fakultät auszudrücken, besteht darin, eine Entfaltung von n in die Liste [1, 2, ..., n] mit der Faltung zu setzen, die es beim Multiplizieren abreißt.
Interessieren Sie sich wirklich für diese spezielle Sprache oder für die gesamte funktionale Programmierung im Allgemeinen? Es ist offensichtlich, dass Ihre Sprache höchstens Polynomzeitalgorithmen ausdrücken kann. System F (polymorpher Lambda-Kalkül) kann Monster wie Ackermanns Funktion mühelos ausdrücken. Selbst wenn Sie sich für Polynom-Zeit-Algorithmen interessieren, benötigen Sie häufig den Super-Polynom-Spielraum, um diese auf natürliche Weise auszudrücken.
Bearbeiten: Wie Dave betont, ist NESL eine der wegweisenden funktionalen datenparallelen Programmiersprachen, aber es ist alles andere als vollständig (es wird nicht einmal versucht). Die APL-Familie hatte eine parallele Evolutionsspur und eine gesamte algebraische Teilmenge (vermeiden Sie die Festkommaoperatoren). Wenn der Schwerpunkt Ihrer Frage auf der Gesamtheit liegt, hat David Turner einige gute Artikel in diesem Bereich verfasst, die sich jedoch nicht speziell mit datenparalleler Programmierung befassen.
quelle