Frage

Ich habe vor kurzem stellte eine Frage über funktionale Programmierung und empfangen (gut!) Antworten, die mehr Fragen (wie scheint der Fall zu sein mit dem Lernen, manchmal) aufgefordert werden. Hier sind ein paar Beispiele:

  1. Eine Antwort bezog sich auf einen Vorteil von unveränderlichen Datenstrukturen: jeder Thread kann seine eigene Kopie haben. Nun, für mich, das klingt eher wie ein Versionskontrollsystem (eine Analogie zu verwenden), wo anstelle Code der Verriegelung, dass jemand ausgecheckt hat, so dass sie nicht von anderen geändert werden können, jeder seine eigenen Kopien überprüfen kann. Klingt gut. Doch in VCS haben Sie das Konzept der „Verschmelzung“ ändert, in dem Fall, dass zwei Menschen das gleiche Material verändert. Es scheint, wie dieses Problem könnte sicherlich in einem multithreaded Szenario kommen ... so wie ist „Fusion“ gemacht, wenn es wichtig ist, dass Themen, die jüngsten Daten sehen?

  2. Diese Antwort über den Fall gesprochen, wo Operationen zu sein wurden in einer Schleife an einem Objekt ausgeführt, und wie können Sie ein neues Objekt jedes Mal durch, anstatt die Aktualisierung einen alten. Aber lassen Sie sich sagen, dass der bankAccount in einem nicht-Loop-Szenario wird aktualisiert - zum Beispiel eines GUI-Bankensystem. Er klickt auf die Schaltfläche „Ändern Interest Rate“ -Schaltfläche, die ein Ereignis ausgelöst wird, das würde (in C # zum Beispiel) so etwas wie bankAccount.InterestRate = newRateFromUser tun. Ich fühle mich wie ich bin dicht, hier zu sein, aber hoffentlich mein Beispiel macht Sinn: es muss eine Möglichkeit geben, dass das Objekt aktualisiert wird, nicht wahr? Mehrere andere Dinge können auf den neuen Daten abhängen.

Wie auch immer, wenn Sie mir, meinen Kopf um den Paradigmenwechsel erhalten helfen, würde ich dankbar sein. Ich erinnere mich an mein Gehirn gehen durch ähnliche „dumme Phasen“, wenn der Codierung OOP nach einem Hintergrund des einfachen Verfahrens zwingend notwendig Ansatz zu lernen.

War es hilfreich?

Lösung

Antwort auf Teil 1: Unveränderliche Objekte tun, indem sie sich nicht so etwas wie „Fusion“ unterstützen die Ergebnisse von zwei Threads Updates kombiniert werden können. Es gibt zwei Hauptstrategien, dass: die pessimistische und die optimistisch. Wenn Sie pessimistisch sind, vorausgesetzt, dass Sie es sehr wahrscheinlich ist, dass zwei Threads die gleichen Daten zur gleichen Zeit aktualisieren wollen. So beschäftigen Sie sperren, so dass der zweite Thread zufrieren wird, bis der erste Thread sagt er fertig ist. Wenn Sie optimistisch sind, dass dies nur selten geschieht, lassen Sie beide Threads auf ihre eigene logische Kopie der Daten arbeiten. Derjenige, der zuerst fertig liefert die neue Version, und der andere hat wieder von vorne zu beginnen - jetzt beginnt erst dann aus den Ergebnissen der ersten Änderungen des Threads. Diese teueren Neustarten geschehen nur gelegentlich aber, so über alle es aufgrund der fehlenden besser abschneidet der Verriegelung (obwohl dies nur wahr ist, wenn Ihr Optimismus gut über platziert wurde, wie selten ein Zusammenstoß auftritt).

Teil 2: Rein funktionale staatenlos Sprachen beseitigen nicht wirklich das Problem. Auch ein reiner kann Haskell Programmzustand mit ihm verbunden ist. Der Unterschied besteht darin, dass Stateful Code einen anderen Rückgabetyp hat. Eine Funktion, Zustand manipuliert wird als eine Folge von Operationen ausgedrückt, die auf ein Objekt arbeiten darstellt diesen Zustand. In einem absurden Beispiel betrachten wir das Dateisystem des Computers. Jedes Mal, wenn ein Programm den Inhalt einer Datei ändert (auch durch ein einzelnes Byte) hat eine neue „Version“ des gesamten Dateisystems erstellt. Und durch die Erweiterung, eine neue Version des gesamten Universums. Aber lassen Sie uns jetzt auf das Dateisystem konzentrieren. Jeder anderer Teil des Programms, das das Dateisystem prüft nun kann die von diesem modifizierten Byte beeinflusst werden. So sagt Haskell, dass die Funktionen auf dem Dateisystem effektiv zu arbeiten um ein Objekt passieren muss eine Version des Dateisystems darstellt. Dann, weil dies sehr mühsam sein würde manuell zu bewältigen, stellt sich die Forderung von innen nach außen und sagt, dass, wenn eine Funktion IO in der Lage sein will, zu tun, es eine Art von Container-Objekt zurückgeben muss. Im Inneren des Behälters ist der Wert der Funktion zurückkehren will. Aber der Behälter dient als Beweis dafür, dass die Funktion auch entweder über Nebenwirkungen oder Nebenwirkungen sehen. Es bedeutet, dass Haskell Typ System in der Lage ist, zwischen den Funktionen-mit-Nebenwirkungen und „reinen“ Funktionen zu unterscheiden. So hilft es, den Statusbehaftung Code enthalten und zu verwalten, ohne es wirklich zu beseitigen.

Andere Tipps

Denken Sie an die String-Klasse in .NET (die ein unveränderliches Objekt ist). Wenn Sie eine Methode auf einer Schnur rufen Sie eine neue Kopie erhalten:

String s1 = "there";
String s2 = s1.Insert(0, "hello ");

Console.Writeline("string 1: " + s1);
Console.Writeline("string 2: " + s2);

Dies wird ausgegeben:

string 1: dort

string 2 ist: Hallo

Vergleich dieses Verhalten zu Stringbuilder, die im Grunde die gleiche Methode Signatur hat:

StringBuilder sb  = new StringBuilder("there");
StringBuilder sb2 = sb.Insert(0, "hi ");

Console.WriteLine("sb 1: " + sb.ToString());
Console.WriteLine("sb 2: " + sb2.ToString());

Da String wandelbar ist, beide Variablen auf das gleiche Objekt zeigen. Der Ausgang wird sein:

sb 1: hallo da

sb 2: hallo da

So können Sie absolut keinen String ändern, sobald Sie es erstellt haben. s1 wird immer „da“ bis zum Ende der Zeit (oder bis zu seinem Müll gesammelt). Das ist wichtig in Threading, weil Sie immer über jedes Zeichen treten können und seinen Wert drucken zu wissen, dass es immer druckt ‚dort‘. Wenn Sie den Druck der String gestartet, nachdem es erstellt wurde, können Sie die ersten beiden Zeichen von dort und get'th‘drucken. Nun stelle man ein anderer Thread kommt Insertionen ‚hallo‘. Der Wert ist jetzt anders! Wenn Sie das dritte Zeichen zu drucken, ist es der Raum von ‚hallo‘. So können Sie drucken:. 'Th gibt es

In Bezug auf # 2 ...

  

Mehrere andere Dinge können auf die abhängig   die neuen Daten.

Dies ist, was die Puristen ein „Effekt“ nennen. Der Begriff der mehr Objektreferenzen auf das gleiche veränderbare Objekt ist das Wesen des wandelbaren Zustandes und der Kern des Problems. In OOP, können Sie ein Objekt „a“ vom Typ Bankkonto haben, und wenn Sie a.Balance oder Dingsbums an verschiedenen lesen mal können Sie verschiedene Werte sehen. Im Gegensatz dazu wird in reinem FP, wenn „a“ hat Bankkonto geben, dann ist es unveränderlich und hat den gleichen Wert unabhängig von der Zeit.

Da jedoch ein Bankkonto ist vermutlich ein Objekt, das wir modellieren wollen, dessen Zustand mit der Zeit variiert, würden wir in FP kodieren, dass die Informationen in der Art. So „a“ könnte Typ „IO Bankaccount“ hat oder eine andere monadischen Art, die Herstellung im Wesentlichen darauf „a“ eigentlich eine Funktion sein, die „der vorherigen Zustand der Welt“ als Eingabe (oder frühere Zustand der Bankzinsen oder was auch immer), und gibt einen neuen Zustand der Welt. den Zinssatz Aktualisierung wäre ein weiterer Betrieb mit einem Typ sein, der den Effekt (zB eine andere IO-Betrieb) darstellt und somit eine neue ‚Welt‘ zurückkehren würde, und alles, was mit einem wäre abhängen kann Daten auf den Zinssatz (Weltstaat) Typ, die es braucht zu nehmen, dass die Welt als Eingabe kennt.

Als Ergebnis der einzig mögliche Weg „a.Balance“ oder Dingsbums zu nennen, ist Code zu verwenden, die dank der statischen Typen erzwingt, dass einige ‚Weltgeschichte, das uns bis jetzt‘ war plombiert richtig der Punkt des Anrufs, und was die Weltgeschichte ist der Eingang wirkt, was zur Folge haben wir von a.Balance erhalten.

Lesen auf dem Staat Monad kann nützlich sein, ein Gefühl zu bekommen wie Sie rein ‚gemeinsamen wandelbar Staat‘ modellieren.

  1. Immutable Datenstrukturen wie VCS nicht. Denken Sie an eine unveränderliche Datenstruktur als eine schreibgeschützte Datei. Wenn es nur lesen, ist es egal, wer zu einem bestimmten Zeitpunkt, welchen Teil der Datei liest, jeder wird die richtige Information lesen.

  2. Diese Antwort spricht über http://en.wikipedia.org/ wiki / Monad_ (functional_programming)

MVCC ( Multiversion Concurrency Control )

Die Lösung für das Problem, Sie beziehen von Rich Hickey beschrieben wird, in seinen Videopräsentationen .

Kurz gesagt: : Statt die Daten vorbei direkt an die Clients durch Bezugnahme, fügen Sie eine weitere Ebene auf indirection und den Verweis auf die Bezugnahme auf die Daten übergeben. ( Na ja, eigentlich möchten Sie mindestens eine weitere Dereferenzierungsebene haben. Aber nehmen wir an, dass die Datenstruktur ist sehr einfach, wie die „Array“. )
Da die Daten unveränderlich ist, sollten die Daten jedes Mal geändert werden, können Sie die Kopie des geänderten Teil erstellen ( im Falle des Arrays sollten Sie ein anderes Array erstellen! ) und Sie erstellen einen anderen Bezug auf alle „geändert“ Daten.
Also für alle Kunden, die die erste Version des Arrays verwendet, verwenden sie den Verweis auf die erste Version. Jeder Kunde versucht, die zweite Version zuzugreifen verwendet die zweite Referenz.
Die „Array“ Datenstruktur ist für diese Methode nicht sehr interessant, weil Sie die Daten nicht aufspalten und Sie sind alles zu kopieren gezwungen. Aber für komplexere Datenstrukturen wie Bäume, können einige Teile der Datenstruktur „geteilt“ werden, so dass Sie nicht gezwungen sind, alles jedes Mal zu kopieren.

Für Details bitte einen Blick auf dieses Papier haben: “ rein funktionale Datenstrukturen " von Chris Okasaki.

"Unveränderliche" bedeutet genau das:. Es ändert sich nicht

Die Art und Weise funktionalen Programme Updates tun ist, um etwa neue Dinge vorbei. Ein bestehender Wert ändert sich nie: Sie gerade einen neuen Wert bauen und dass stattdessen übergeben. Sehr oft wird der neue Wert Aktien Zustand mit dem alten; gute Beispiele für die Technik sind Listen von cons Zellen zusammengesetzt, und die Reißverschluss .

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top