2016-11-30 3 views
2

Beim Versuch, Begleitobjekte zu verstehen, habe ich folgenden Code geschrieben, der die Anzahl der Instanzen einer Klasse zählt. Ich musste eine 'Var' verwenden, um die Zählung zu behalten. Gibt es eine "funktionale Programmierung", um dieselbe Aufgabe zu erfüllen, d. H. Unveränderliche Variablen zu verwenden.Wie man einen Zähler in funktionaler Programmierweise implementiert

class C { 
    C.counter+=1 
    def someCFunction = {println ("some C function. Counter is "+C.counter)} 
} 

object C{ 
    var counter:Int=0 //I do not want to use var 
} 

val c1 = new C 
c1.someCFunction 

val c2 = new C 
c2.someCFunction 
+0

Funktoren oder State Monad scheint eine gute Passform – naomik

Antwort

3

Eine der großen Eigenschaften eines rein funktionalen Programm, das wandelbar Variablen und andere Nebenwirkungen vermeidet, dass der Wert, dass ein Ausdruck nur auf dem Ausdruck selbst abhängt auswertet. Es kommt nicht darauf an, in welcher Reihenfolge die Dinge ausgewertet werden (von links nach rechts, von links nach rechts, strikt, faul), der Zustand des Betriebssystems, Uhrzeit etc.

Insbesondere bedeutet das, dass rein Funktionseinstellung Jeder Aufruf an new C gibt ein vollständig identisches Zählerobjekt zurück. Dies ist in der Regel eine gute Sache, weil es es einfacher macht, über Ihr Programm nachzudenken, aber es behindert irgendwie, was Sie dort zu tun versuchen. Um die C-Objekte anders zu machen, müssten Sie ihre Zählerwerte explizit übergeben, was, um ehrlich zu sein, das Problem einfach unter den Teppich wirft.

val c1 = new C(0) 
val c2 = new C(1) 

Wenn Sie einen globalen „counter“ Variable wie die interne Klassenvariable haben Sie eine Möglichkeit unter Verwendung wurden sie in einem rein funktionalen Rahmen zu realisieren wäre, den Zählerwert für jede Funktion zu übergeben, die ein benötigt Zähler und haben diese Funktionen auch die aktualisierte Version des Zählers. Für ein kurzes Beispiel:

def increment_counter(n: Int): Int = { n + 1) 

def create_c(n: Int): (C, Int) = { 
    val c = new C(n) 
    val n' = increment_counter n 
    (c, n') 
} 

val n = 0 
val (c1, n') = create_c(n) 
val (c2, n'') = create_c(n') 
val n' = increment_counter(n) 

können Sie strukturieren das ein bisschen besser mit einem Staat Monad Muster (die meisten Einführungen in Monaden wahrscheinlich dies als Beispiel haben).

Es ist jedoch sehr wahrscheinlich, dass es komplizierter wird, als nur eine veränderbare Variable für den Zähler zu verwenden. Tatsächlich benutze ich normalerweise veränderbare Variablen für diese "global increasing counters" in den funktionalen Sprachen, die mir erlauben, dies zu tun.

1

Es ist nicht so, dass var von Natur aus eine schlechte Sache ist. Es ist in der Sprache aus einem Grund. Es ist nur so, dass das, was es repräsentiert, eine Entität, die irgendeine Form von veränderbarem Zustand beibehält, wenn möglich vermieden werden sollte. Wenn es nicht vermieden werden kann, wenn der Entwurf eine laufende Summe von Klasseninstanzen erfordert, sollte sein Umfang so beschränkt wie möglich sein.

class C private { // private constructor, can only use factory method 
    def someCFunction = {println ("some C function. Counter is "+ C.getCount())} 
} 
object C{ 
    private[this] var counter:Int = 0 // not even companions can see this 
    def apply() = {counter += 1; new C} // factory method 
    def getCount() = counter    // accessor method 
} 

val c1 = C() 
c1.someCFunction 

val c2 = C() 
c2.someCFunction 
2

Dies ist ein guter Anwendungsfall für die State Monad. Anstatt eine Variable an Ort und Stelle zu ändern, erstellen Sie einen neuen Wert und übergeben ihn.

import cats.data.State 
class C {} 
object C { val counter: State[Int, Unit] = State.pure() } 

def createNewC: State[Int, C] = { 
    // increment the count, and return a new instance of C 
    C.counter.modify(_ + 1).map(_ => new C) 
} 

val countAll = for { 
    c0 <- createNewC 
    c1 <- createNewC 
    c2 <- createNewC 
    c3 <- createNewC 
} yield { 
    // use your instance of C in here 
() 
} 

// actually run your program, start the counter at 0 
countAll.run(0).value._1 // 4 

Hinweis: Hier kommt der Status aus dem Cats-Projekt.

+0

So ist Ihr Zustand "Einheit", die nicht viel Sinn macht. Ich denke du willst 'State [Int, Unit]' stattdessen und benutze etwas wie 'State.modify (_ + 1)' in der 'countCalls' Funktion? –

+0

Es macht mehr Sinn, die State Monade wie beschrieben zu verwenden (ich habe meine Antwort aktualisiert). – soote

Verwandte Themen