2012-02-04 12 views
24

Rozważmy prosty przykład mapowania:Jak uzyskać indeks elementu podczas mapowania tablicy w Scali?


    val a = Array("One", "Two", "Three") 
    val b = a.map(s => myFn(s)) 

Co potrzebne jest użycie nie myFn(s: String): String tutaj, ale myFn(s: String, n: Int): String, gdzie n byłby indeks s w a. W tym konkretnym przypadku myFn oczekiwałby, że drugi argument będzie równy 0 dla s == "Jeden", 1 dla s == "Dwa" i 2 dla s == "Trzy". Jak mogę to osiągnąć?

Odpowiedz

66

Zależy od tego, czy chcesz wygodę, czy szybkość.

Powolne:

a.zipWithIndex.map{ case (s,i) => myFn(s,i) } 

Szybsze:

Możliwie najszybciej:

Array.tabulate(a.length){ i => myFn(a(i),i) } 

Jeśli nie, to na pewno jest:

val b = new Array[Whatever](a.length) 
var i = 0 
while (i < a.length) { 
    b(i) = myFn(a(i),i) 
    i += 1 
} 

(W Scala 2.10.1 z Javą 1.6u37, jeśli "prawdopodobnie najszybszy" jest deklarowany do zrobienia 1x raz dla trywialnej operacji na łańcuchu (obcięcie długiego ciągu do kilku znaków), to "slow" trwa 2x dłużej, "szybciej" każde z nich zajmuje 1.3 razy dłużej, a "z pewnością" zajmuje tylko 0,5x czasu.)

+17

Lubię czytać swoje odpowiedzi na szybkość rzeczy, ale może być tak przygnębiające czasami ... :-) –

+0

Dzięki. Całkiem miły wybór. – Ivan

+0

Jaka jest szybkość "a.view.zipWithIndex.map {case (s, i) => myFn (s, i)}" w porównaniu do twoich rozwiązań? – gzm0

4

Ogólna wskazówka: Użyj metody .iterator, aby uniknąć tworzenia kolekcji pośrednich, a tym samym przyspieszyć obliczenia. (Tylko gdy wymagania eksploatacyjne wymaga. Albo nie.)

scala> def myFun(s: String, i: Int) = s + i 
myFun: (s: String, i: Int)java.lang.String 

scala> Array("nami", "zoro", "usopp") 
res17: Array[java.lang.String] = Array(nami, zoro, usopp) 

scala> res17.iterator.zipWithIndex 
res19: java.lang.Object with Iterator[(java.lang.String, Int)]{def idx: Int; def idx_=(x$1: Int): Unit} = non-empty iterator 

scala> res19 map { case (k, v) => myFun(k, v) } 
res22: Iterator[java.lang.String] = non-empty iterator 

scala> res22.toArray 
res23: Array[java.lang.String] = Array(nami0, zoro1, usopp2) 

Należy pamiętać, że iteratory są zmienne, a więc gdy spożywane nie mogą być ponownie użyte.


Na marginesie: Powyższy map połączenia wymaga zakończenia tupling a następnie funkcja aplikacji. Wymusza to użycie niektórych zmiennych lokalnych. Możesz tego uniknąć używając czarów wyższego rzędu - zamień zwykłą funkcję na akceptującą krotkę, a następnie przekaż ją do map.

scala> Array("nami", "zoro", "usopp").zipWithIndex.map(Function.tupled(myFun)) 
res24: Array[java.lang.String] = Array(nami0, zoro1, usopp2) 
+0

"iteratory są zmienne, a więc raz zużyte nie mogą być użyte ponownie" - kiedyś spędziłem trochę czasu na debugowaniu, dlaczego program nie działa, aby znaleźć iterator, który zostanie użyty pusty raz :-) – Ivan

3

Co z tym? Myślę, że powinna być szybka i ładna. Ale nie jestem ekspertem na szybkość Scala ...

a.foldLeft(0) ((i, x) => {myFn(x, i); i + 1;}) 
Powiązane problemy