Was ist ein Kombinator?
Ist es "eine Funktion oder Definition ohne freie Variablen" (wie in SO definiert)?
Oder wie wäre es damit: Laut John Hughes in seinem bekannten Artikel über Pfeile ist "ein Kombinator eine Funktion, die Programmfragmente aus Programmfragmenten erstellt" , was vorteilhaft ist, weil "... der Programmierer, der Kombinatoren verwendet, einen Großteil der gewünschten Konstrukteure konstruiert automatisch programmieren, anstatt jedes Detail von Hand zu schreiben ". Er fährt fort zu sagen , dass map
und filter
sind zwei typische Beispiele für solche combinators.
Einige Kombinatoren, die der ersten Definition entsprechen:
- S.
- K.
- Y.
- andere von To Mock a Mockingbird (Ich kann mich irren - ich habe dieses Buch nicht gelesen)
Einige Kombinatoren, die der zweiten Definition entsprechen:
- Karte
- Filter
- falten / reduzieren (vermutlich)
- eine von >> =, komponieren, fmap ?????
Ich bin nicht an der ersten Definition interessiert - diese würden mir nicht helfen, ein echtes Programm zu schreiben (+1, wenn Sie mich davon überzeugen, dass ich falsch liege). Bitte helfen Sie mir, die zweite Definition zu verstehen . Ich denke, Map, Filter und Reduce sind nützlich: Sie ermöglichen es mir, auf einer höheren Ebene zu programmieren - weniger Fehler, kürzerer und klarerer Code. Hier sind einige meiner spezifischen Fragen zu Kombinatoren:
- Was sind weitere Beispiele für Kombinatoren wie Map, Filter?
- Welche Kombinatoren implementieren Programmiersprachen häufig?
- Wie können Kombinatoren mir helfen, eine bessere API zu entwerfen?
- Wie entwerfe ich effektive Kombinatoren?
- Was ähneln Kombinatoren in einer nicht funktionierenden Sprache (z. B. Java) oder was verwenden diese Sprachen anstelle von Kombinatoren?
Aktualisieren
Dank @CA McCann habe ich jetzt ein etwas besseres Verständnis für Kombinatoren. Aber eine Frage ist für mich immer noch ein Knackpunkt:
Was ist der Unterschied zwischen einem Funktionsprogramm, das mit und ohne starken Einsatz von Kombinatoren geschrieben wurde?
Ich vermute, die Antwort ist, dass die kombinatorlastige Version kürzer, klarer und allgemeiner ist, aber ich würde mich über eine eingehendere Diskussion freuen, wenn dies möglich ist.
Ich suche auch nach mehr Beispielen und Erklärungen für komplexe Kombinatoren (dh komplexer als fold
) in gängigen Programmiersprachen.
quelle
Antworten:
Die beiden Definitionen sind im Grunde dasselbe. Die erste basiert auf der formalen Definition und die Beispiele, die Sie geben, sind primitive Kombinatoren - die kleinstmöglichen Bausteine. Sie können Ihnen helfen, ein echtes Programm zu schreiben, indem Sie mit ihnen ausgefeiltere Kombinatoren bauen können. Stellen Sie sich Kombinatoren wie S und K als die Maschinensprache eines hypothetischen "kombinatorischen Computers" vor. Tatsächliche Computer funktionieren natürlich nicht so. In der Praxis werden Operationen auf höherer Ebene normalerweise auf andere Weise hinter den Kulissen implementiert, aber die konzeptionelle Grundlage ist immer noch ein nützliches Werkzeug, um die Bedeutung dieser übergeordneten Ebenen zu verstehen Operationen.
Die zweite Definition, die Sie geben, ist informeller und bezieht sich auf die Verwendung komplexerer Kombinatoren in Form von Funktionen höherer Ordnung, die andere Funktionen auf verschiedene Weise kombinieren. Beachten Sie, dass, wenn die Grundbausteine die oben genannten primitiven Kombinatoren sind, alles , was daraus aufgebaut ist, eine Funktion höherer Ordnung und auch ein Kombinator ist. In einer Sprache, in der andere Grundelemente existieren, wird jedoch zwischen Dingen unterschieden, die Funktionen sind oder nicht. In diesem Fall wird ein Kombinator normalerweise als eine Funktion definiert, die andere Funktionen allgemein manipuliert, anstatt mit Nichtfunktionen zu arbeiten Dinge direkt funktionieren.
Viel zu viele, um sie aufzulisten! Beide wandeln eine Funktion, die das Verhalten eines einzelnen Werts beschreibt, in eine Funktion um, die das Verhalten einer gesamten Sammlung beschreibt. Sie können auch Funktionen haben, die nur andere Funktionen transformieren , z. B. das Ende-zu-Ende-Erstellen oder das Teilen und Rekombinieren von Argumenten. Sie können Kombinatoren haben, die Einzelschrittoperationen in rekursive Operationen umwandeln, die Sammlungen erzeugen oder verbrauchen. Oder wirklich alle möglichen anderen Dinge.
Das wird ziemlich unterschiedlich sein. Es gibt relativ wenige vollständig generische Kombinatoren - meistens die oben genannten primitiven -, so dass Kombinatoren in den meisten Fällen ein gewisses Bewusstsein dafür haben, welche Datenstrukturen verwendet werden (selbst wenn diese Datenstrukturen ohnehin aus anderen Kombinatoren aufgebaut sind), in denen In diesem Fall gibt es normalerweise eine Handvoll "vollständig generischer" Kombinatoren und dann, was auch immer verschiedene spezielle Formen sind, für die sich jemand entschieden hat. Es gibt eine lächerliche Anzahl von Fällen, in denen (entsprechend verallgemeinerte Versionen von) Zuordnen, Falten und Entfalten ausreichen, um fast alles zu tun, was Sie möchten.
Genau wie Sie sagten, indem Sie in Bezug auf Operationen auf hoher Ebene und die Art und Weise, wie diese interagieren, anstelle von Details auf niedriger Ebene denken.
Denken Sie an die Beliebtheit von "for each" -Stilschleifen über Sammlungen, mit denen Sie die Details der Aufzählung einer Sammlung abstrahieren können. Dies sind in den meisten Fällen nur Map / Fold-Operationen. Wenn Sie einen Kombinator (anstelle einer integrierten Syntax) verwenden, können Sie beispielsweise zwei vorhandene Schleifen verwenden und diese auf mehrere Arten direkt kombinieren - ineinander verschachteln. Tun Sie dies nacheinander und so weiter - indem Sie einfach einen Kombinator anwenden, anstatt eine ganze Menge Code herumzujonglieren.
Überlegen Sie sich zunächst, welche Vorgänge für die von Ihrem Programm verwendeten Daten sinnvoll sind. Überlegen Sie sich dann, wie diese Operationen auf generische Weise sinnvoll kombiniert werden können und wie Operationen in kleinere Teile zerlegt werden können, die wieder miteinander verbunden sind. Die Hauptsache ist, mit Transformationen und Operationen zu arbeiten , nicht mit direkten Aktionen . Wenn Sie eine Funktion haben, die nur einige komplizierte Funktionen auf undurchsichtige Weise ausführt und nur ein vorverdautes Ergebnis ausspuckt, können Sie damit nicht viel tun. Überlassen Sie die endgültigen Ergebnisse dem Code, der die Kombinatoren verwendet - Sie möchten Dinge, die Sie von Punkt A nach Punkt B führen, nicht Dinge, die den Beginn oder das Ende eines Prozesses erwarten.
Ahahahaha. Lustig sollte man fragen, denn Objekte sind in erster Linie Dinge höherer Ordnung - sie haben einige Daten, aber sie führen auch eine Reihe von Operationen mit sich, und vieles, was ein gutes OOP-Design ausmacht, läuft darauf hinaus, dass Objekte "sollten" wirken normalerweise wie Kombinatoren, nicht wie Datenstrukturen ".
Die wahrscheinlich beste Antwort hier ist also, dass sie anstelle kombinatorähnlicher Dinge Klassen mit vielen Getter- und Setter-Methoden oder öffentlichen Feldern verwenden und eine Logik, die hauptsächlich darin besteht, eine undurchsichtige, vordefinierte Aktion auszuführen.
quelle