Zähler werden jedes Mal initialisiert?

7

Ich versuche, einen einfachen Zähler zu machen. Meine Zähler gehen jedoch nicht hoch. Es scheint mir so, als ob sie jedes Mal durch die Funktion "inc" neu initialisiert werden oder vielleicht (n + 1) nicht funktioniert. Wie kann ich das am besten beheben?

%Vor%     
J Fritsch 21.11.2011, 22:10
quelle

4 Antworten

21

Obwohl veränderbare Variablen in Haskell verwendet werden können, wie andere Kommentatoren zeigen, ist es kein guter Stil: Mutationen sollten in den meisten Fällen nicht verwendet werden.

Die Funktion inc akzeptiert ihr Argument nach Wert, dh sie ändert ihr Argument nicht. Außerdem behalten die von let deklarierten Variablen ihre Anfangswerte bei, so dass Sie sie nicht ändern können.

Wie schreibt man, wenn keine Variable jemals geändert werden kann? Die Antwort ist:

  1. Anstatt etwas an Ort und Stelle zu ändern, geben Sie einen neuen Wert zurück
  2. für Schleifen, verwenden Sie Rekursion

Glücklicherweise müssen Sie die Rekursion selten selbst schreiben, da sich die meisten rekursiven Muster bereits in der Standardbibliothek befinden.

In Ihrem Fall müssen Sie mehrere E / A-Aktionen durchführen und den endgültigen Wert der beiden Zähler zurückgeben. Beginnen wir mit einer Aktion:

%Vor%

Hier deklarieren wir eine lokale Funktion mit 2 Parametern: die aktuellen Werte der Zähler, verpackt in einem Tupel (Int, Int) (eine Struktur in anderen Sprachen) und aktueller Iteration Int . Die Funktion führt IO-Aktionen durch und gibt modifizierte Werte der Zähler IO (Int, Int) zurück. Dies alles wird in seinem Typ angezeigt:

%Vor%

ping gibt einen Wert von IO String type zurück. Um es zu vergleichen, benötigen Sie ein String ohne IO . Um dies zu tun, sollten Sie >>= function:

verwenden %Vor%

Da dieses Muster häufig ist, kann es so geschrieben werden

%Vor%

Aber die Bedeutung ist genau die gleiche (Compiler übersetzt do Notation in Anwendungen von >>= ).

Die Verarbeitung zeigt einige häufigere Muster:

%Vor%

Hier ist if kein Imperativ if , sondern eher ein ternärer condition ? value1 : value2 Operator in anderen Sprachen. Beachten Sie auch, dass unsere Funktion tryOnePing (c, f) akzeptiert und entweder (c+1, f) oder (c, f+1) zurückgibt. Wir haben Tupel benutzt, da wir nur mit 2 Zählern arbeiten müssen. Bei einer großen Anzahl von Zählern müssten wir einen Strukturtyp deklarieren und benannte Felder verwenden.

Der Wert des gesamten If-Konstrukts ist ein Tupel (Int, Int). ping ist eine IO-Aktion, also muss tryOnePing auch eine IO-Aktion sein. Die Funktion return ist keine zwingende Rückgabe, sondern eine Möglichkeit, (Int, Int) in IO (Int, Int) zu konvertieren.

Also, wie wir tryOnePing haben, müssen wir eine Schleife schreiben, um es 1000 Mal auszuführen. Ihre forM_ war keine gute Wahl:

  1. Es gibt keine zwei Zähler zwischen Iterationen
  2. _ gibt an, dass der letzte Wert der Zähler entfernt wird, anstatt ihn zurückzugeben

Sie brauchen hier nicht forM_ , sondern foldM

%Vor%

foldM führt eine IO-Aktion aus, die von jedem Element der Liste parametrisiert wird, und übergibt einen bestimmten Zustand zwischen Iterationen, in unserem Fall die beiden Zähler. Es akzeptiert den Anfangszustand und gibt den Endzustand zurück. Da IO-Aktionen ausgeführt werden, gibt es natürlich IO (Int, Int) zurück, also müssen wir >>= verwenden, um es erneut zur Anzeige zu extrahieren:

%Vor%

In Haskell können Sie so genannte "eta reductions" durchführen, dh Sie können dieselben Bezeichner von beiden Seiten einer Funktionsdeklaration entfernen. Z.B. \foo -> bar foo ist identisch mit nur bar . Also in diesem Fall mit >>= kannst du schreiben:

%Vor%

ist viel kürzer als do notation:

%Vor%

Beachten Sie auch, dass Sie nicht zwei Zähler haben müssen: Wenn Sie 3000 Erfolge haben, dann haben Sie 7000 Fehler. So wird der Code:

%Vor%

Schließlich ist es in Haskell gut, IO-Aktionen von Nicht-IO-Code zu trennen. Daher ist es besser, alle Ergebnisse von Pings in einer Liste zu sammeln und dann erfolgreiche Pings darin zu zählen:

%Vor%

Beachten Sie, dass wir vermieden haben, insgesamt zu erhöhen.

Es kann noch kürzer geschrieben werden, erfordert aber mehr Geschick beim Lesen und Schreiben. Mach dir keine Sorgen, du wirst diese Tricks bald lernen:

%Vor%     
nponeccop 21.11.2011, 23:32
quelle
7

In Haskell sind Daten standardmäßig unveränderlich. Dies bedeutet, dass c in inc c immer Null ist.

Um veränderbare Variablen in Haskell zu erhalten, müssen Sie explizit nach fragen , dh mit IORefs . Mit ihnen könnten Sie etwas schreiben wie:

%Vor%     
bzn 21.11.2011 22:34
quelle
5

Genau wie in Code außerhalb von IO können Sie eine Reihe von Berechnungen mithilfe einer Faltung aneinanderreihen. foldM funktioniert innerhalb einer Monade, zB

%Vor%     
ephemient 21.11.2011 22:36
quelle
2

Variablen sind in Haskell unveränderlich. Wenn Sie inc f aufrufen, wird ein Wert von 0 + 1 zurückgegeben, den Sie sofort ignorieren. Der Wert von f ist 0 und bleibt dies für alle Zeit.

    
Thomas M. DuBuisson 21.11.2011 22:36
quelle

Tags und Links