2013-03-05 18 views
6

Mam kodu Scala jak toZwiększanie zmiennej w foldLeft

var i = 1 
for(e <- array) { 
    acc += e * i 
    i += 1 
} 

muszę mnożenie pierwszego elementu w tablicy 1, następny przez 2, następnego dnia 3 i tak dalej, dodając je wszystkie w akumulator. Czuję, że jest lepszy sposób na zrobienie tego w Scali, może nawet ze złożeniem?

Odpowiedz

5
val x = List(1,1,1,1,1,1) 
(((0,1) /: x){case ((acc, mult), l) => (acc + (l * mult), mult + 1) })._1 

Innymi słowy, zaczynając od akumulatora 0 i mnożnik 1, złóż każdy element w liście, zmieniając akumulator do acc + (l * mult) i zwiększając mnożnik o 1. Otrzymujemy ostateczny mnożnik się na koniec również, więc nazywamy ._1, aby uzyskać akumulator.

Edytuj: Jako że @RexKerr wskazuje w swojej odpowiedzi poniżej (i komentarz), jeśli wydajność jest poważnym problemem, lepiej jest użyć metody jawnej rekursywnej.

+0

zdumiewającą odpowiedź, dziękuję! – chrissphinx

10

Wolę zipWithIndex który jest prostszy do czytania:

array.zipWithIndex.map { case (e, i) => e * (i + 1) }.sum 
+0

ale potem iterujesz po tablicy dwa razy od mojego zrozumienia tej metody, prawda? – chrissphinx

+0

Trzy razy. zipWithIndex i mapują zarówno iteracyjne względem tablicy, jak i sumę zaimplementowaną za pomocą metody foldLeft. Więc moje podejście z pewnością nie jest najszybsze – maxmc

+0

zdecydowanie bardziej czytelne, jednak – chrissphinx

2

Nie jestem pewien, czy to, co proponuję jest lepszy sposób to zrobić, czy nie, ponieważ jest bardziej funkcjonalny (== będzie wykonywać wolniej)

(0 /: (array zipWithIndex)) {(a, i) => (i._1 * (i._2 + 1)) + a}

ten wykonuje foldLeft na macierzy, która jest wytwarzana metodą zipWithIndex w http://www.scala-lang.org/api/current/index.html#scala.Array

zipWithIndex po prostu zamienia elementy kolekcji w ich indeksy.

15

"Lepsze" zależy od tego, jakie są Twoje cele. Krótki i wyraźny? Prawdopodobnie

{ for (i <- array.indices; e = array(i)) yield (i+1)*e }.sum 

lub

array.indices.map(i => (i+1)*array(i)).sum 

(lub nieco szybciej, ponieważ tworzenie związków pośrednich, jak przejść:

array.indices.iterator.map(i => (i+1)*array(i)).sum 

).

Powinieneś zazwyczaj być krótki i czysty.

Szybko? Następnie trzeba przejść starej szkoły:

var i = 0 
var acc = 0 
while (i < array.length) { 
    acc += (i+1)*array(i) 
    i += 1 
} 

lub stosowanie rekurencji

def sum(a: Array[Int], i: Int = 0, acc: Int = 0): Int = 
    if (i >= a.length) acc else sum(a, i+1, (i+1)*a(i) + acc) 
sum(array) 
+0

Czy rekurencja jawna może być szybsza niż spasowanie? Jedyną zaletą, jaką widzę, nie jest przydzielanie krotki ... – Impredicative

+4

@Impredicative - Jeśli robisz lekkie operacje, takie jak dodawanie liczb całkowitych, nieprzydzielanie krotki jest ogromne (10-krotne przyspieszenie). Ponadto, kolekcje nie specjalizują się w prymitywach, co oznacza, że ​​każda liczba całkowita musi być pudełkowa, a rekursja może używać typów pierwotnych. Również ogromny (oba razem dają przyspieszenie ~ 20x-30x). –

+0

Oba bardzo dobre punkty! – Impredicative

Powiązane problemy