2011-07-25 15 views
48

Załóżmy, że mam dwie tablice:Uzyskaj indeks bieżącego elementu w metodzie foreach funkcji Traversable?

val ar1 = Array[String]("1", "2", "3") 
val ar2 = Array[String]("1", "2", "3", "4") 

Teraz dla każdego elementu ar1, chcę najpierw łączyć ten element z odpowiednim elementem ar2, a następnie wydrukować wynik. Jednym ze sposobów byłoby zrobić coś takiego:

List.range(0, ar1.size).foreach(i => println(ar1(i)+ar2(i))) 

To byłby ładniejszy jeśli było foreach wariant, który pozwoliłby mi pracować bezpośrednio z indeksów ar1 zamiast najpierw buduje listę całkowitą.

Być może jest lepszy sposób?

+0

Zobacz także http://stackoverflow.com/questions/6833501/efficient-iteration-with-index-in-scala – Vadzim

Odpowiedz

84

Jednym bardzo wygodnym sposobem na to jest metoda zipped na krotkach. Umieść dwie kolekcje, wydostań dwa argumenty do funkcji!

(ar1,ar2).zipped.foreach((x,y) => println(x+y)) 

ta jest wygodna do pisania i szybko, gdyż nie trzeba budować krotka do przechowywania każdej pary (tak jak z (ar1 zip ar2)), które następnie trzeba rozbierać ponownie. Obie formy zip zatrzymują się, gdy krótszy z dwóch zbiorów jest wyczerpany.

Jeśli masz coś bardziej skomplikowanego (np trzeba zrobić matematyki na indeksie), roztwór kanoniczna jest zip w indeksie:

ar1.zipWithIndex.foreach{ case(x,i) => println(x+ar2(i)) } 

Sposób używasz jest szybciej i zwięźle wykonanej w następujący sposób może być użyteczne:

ar1.indices.foreach(i => println(ar1(i)+ar2(i))) 

chociaż działa to tylko wtedy, gdy pierwsza kolekcja nie jest dłuższa niż druga. Możesz również wyraźnie określić swoje zakresy:

(0 until (ar1.size min ar2.size)).foreach(i => println(ar1(i)+ar2(i))) 

aby obejść ten problem. (Możesz zobaczyć, dlaczego są preferowane, chyba że to, co robisz, jest zbyt skomplikowane, aby to ułatwić.)

Jeśli nie jest to zbiór równolegle (i zwykle nie jest to chyba nazwać .par), jest to również możliwe, choć nie zaleca, aby śledzić z zmienny zmiennej:

{ var i=-1; ar1.foreach{ x => i += 1; println(x+ar2(i)) } } 

istnieje bardzo ograniczona liczba przypadków, w których jest to konieczne (np. jeśli chcesz pominąć lub cofnąć się do niektórych innych kolekcji); jeśli możesz tego uniknąć, zwykle będziesz miał kod, o którym łatwiej się zastanowić.

+1

Nie odpowiadam na to pytanie, ponieważ pytanie w sposób jednoznaczny dotyczy 'Traversable', a' Traversable' nie ma 'zipWithIndex' - co jest * denerwujące *. –

+0

@WilfredSpringer - Pytanie _nie_ pyta o 'Traversable', tylko sugeruje, że chce użyć' foreach' w pewnym momencie. –

+0

Nie zrozum mnie źle. Będę głosować w górę twoją odpowiedź. Po prostu mówię, że pytanie brzmi: "Uzyskaj indeks bieżącego elementu w metodzie foreach * Traversable *?" Tak więc wydaje się, że punktem wyjścia jest "Traversable"; przynajmniej tak * * czytam pytanie. –

4

ja nie miałem okazji go przetestować, ale to powinno wystarczyć:

ar1.zip(ar2).foreach(x => println(x._1 +x._2)) 
+0

Naprawiono dwukrotne użycie '_' przez dodanie zmiennej. –

3

zip to zrobi:

ar1 zip ar2 foreach { p => println(p._1 + p._2) } 

ta przyniesie:

11 
22 
33 

Zauważ, że nie potrzebujesz [String] rodzaju ogólnego, będzie wnioskowane przez kompilator:

val ar1 = Array("1", "2", "3") 
val ar2 = Array("1", "2", "3", "4") 
34

To jest jak pętla z indeksem w idiomatycznym Scala:

scala> List("A", "B", "C").zipWithIndex foreach { case(el, i) => 
    | println(i + ": " + el) 
    | } 
0: A 
1: B 
2: C 

I tu jest idiomatyczne Scala sposób zrobić to, co staramy się osiągnąć w kodzie:

scala> val arr1 = Array("1", "2", "3") 
arr1: Array[java.lang.String] = Array(1, 2, 3) 

scala> val arr2 = Array("1", "2", "3", "4") 
arr2: Array[java.lang.String] = Array(1, 2, 3, 4) 

scala> (arr1, arr2).zipped.map(_ + _) foreach println 
11 
22 
33 
+0

to również bardzo dobra odpowiedź. – Jus12

Powiązane problemy