2010-11-24 8 views
4

Ich bin ziemlich neu in Scala, aber ich mag es zu wissen, was die bevorzugte Lösung dieses Problems ist. Angenommen, ich habe eine Liste von Artikeln und möchte den Gesamtbetrag der Schecks wissen. Ich könnte etwas tun, wie so:Scala Vals vs Vars

val total = items.filter(_.itemType == CHECK).map(._amount).sum 

Das würde mich, was ich brauche, die Summe aller Kontrollen in einer unveränderlichen Variablen. Aber es macht es mit dem, was wie 3 Iterationen scheint. Einmal um die Schecks zu filtern, erneut um die Beträge und dann die Summe abzubilden. Eine andere Möglichkeit wäre, wie etwas zu tun:

var total = new BigDecimal(0) 
for (
    item <- items 
    if item.itemType == CHECK 
) total += item.amount 

Das gibt mir das gleiche Ergebnis, aber mit 1 Iteration und eine veränderbare Variable, die zu fein scheint. Aber wenn ich mehr Informationen extrahieren möchte, sagen wir die Gesamtzahl der Überprüfungen, würde das mehr Zähler oder änderbare Variablen erfordern, aber ich müsste die Liste nicht noch einmal durchlaufen. Scheint nicht wie die "funktionale" Art zu erreichen, was ich brauche.

var numOfChecks = 0 
var total = new BigDecimal(0) 
items.foreach { item => 
    if (item.itemType == CHECK) { 
     numOfChecks += 1 
     total += item.amount 
    } 
} 

Also, wenn Sie sich um eine Reihe von Zählern oder Summen auf einer Liste zu finden ist es bevorzugt, wandelbar Variablen zu halten oder sich darüber keine Sorgen entlang der Linien von etwas tun:

val checks = items.filter(_.itemType == CHECK) 
val total = checks.map(_.amount).sum 
return (checks.size, total) 

die scheint leichter zu lesen und verwendet nur vals

+1

Der 'for' Ausdruck wird tatsächlich zu einer' map', einem 'filter' und einer' foreach' kompiliert, das entspricht also Ihrem ersten Beispiel. – Theo

+1

Dies ist nicht mehr korrekt: das für Verständnis verwendet mit Filter, nicht Filter, es sei denn, die Sammlung hat keine withFilter-Methode. – extempore

Antwort

6

Sie foldLeft dafür verwenden:

(0 /: items) ((total, item) => 
    if(item.itemType == CHECK) 
     total + item.amount 
    else 
     total 
) 

Der folgende Code wird ein Tupel (Anzahl der Kontrollen -> Summe der Beträge) zurück:

((0, 0) /: items) ((total, item) => 
    if(item.itemType == CHECK) 
     (total._1 + 1, total._2 + item.amount) 
    else 
     total 
) 
+0

vergaß das Falten/das würde mir alle meine Daten wieder in einem netten Tupel geben –

8

Eine andere Möglichkeit, das Problem in einer Iteration zu lösen wäre Ansichten oder Iteratoren zu verwenden:

items.iterator.filter(_.itemType == CHECK).map(._amount).sum 

oder

items.view.filter(_.itemType == CHECK).map(._amount).sum 

diese Weise wird die Auswertung des Ausdrucks, bis der Anruf von sum verzögert.

Wenn Ihre Einzelteile Fallklassen sind, könnten Sie es auch so schreiben:

items.iterator collect { case Item(amount, CHECK) => amount } sum 
+0

danke/ich war mir der 'view'-Methode nicht bewusst –

7

Ich finde, dies zu tun „drei Iterationen“ spricht ein wenig irreführend ist - immerhin jede Iteration tut weniger Arbeit als eine einzige Iteration mit allem. Es folgt also nicht automatisch, dass das dreimalige Iterieren länger dauert als das einmalige Iterieren.

Erstellen temporäre Objekte, jetzt , dass ist ein Anliegen, weil Sie schlagen Speicher (auch wenn zwischengespeichert), was nicht der Fall der einzelnen Iteration ist. In diesen Fällen hilft view, obwohl es weitere Methodenaufrufe hinzufügt, um die gleiche Arbeit zu erledigen. Hoffentlich wird JVM das optimieren. Weitere Informationen zu Ansichten finden Sie unter Moritzanswer.