2011-09-08 15 views
9

Podczas pracy nad "Scala dla zniecierpliwionych" zastanawiałem się: czy można użyć pętli Scala bez sekwencji?Dla pętli w scala bez sekwencji?

Na przykład w książce znajduje się ćwiczenie z prośbą o zbudowanie obiektu licznika, którego nie można zwiększyć o wartość Integer.MAX_VALUE. W celu przetestowania moje rozwiązanie, napisałem poniższy kod:

var c = new Counter 
for(i <- 0 to Integer.MAX_VALUE) c.increment() 

ten zgłasza błąd: sekwencje nie może zawierać więcej niż elementów Int.MaxValue. Wydaje mi się, że oznacza to, że Scala po raz pierwszy przydziela i wypełnia obiekt sekwencji, z wartościami od 0 do liczby całkowitej.MaxValue, a następnie wykonuje pętlę foreach na tym obiekcie sekwencji.

Zdaję sobie sprawę, że mogę to zrobić w zamian:

var c = new Counter 
while(c.value < Integer.MAX_VALUE) c.increment() 

Ale czy jest jakiś sposób aby zrobić tradycyjną stylu C do pętli z pętli for?

Odpowiedz

17

W rzeczywistości 0 to N faktycznie nie zapełnić coś z liczb całkowitych od 0 do N. Zamiast tego tworzy instancję scala.collection.immutable.Range, która stosuje swoje metody do wszystkich liczb całkowitych generowanych w locie.

Wystąpił błąd tylko dlatego, że musisz być w stanie dopasować liczbę elementów (czy faktycznie istnieją, czy nie) do dodatniej części Int w celu utrzymania umowy na metodę length. 1 to Int.MaxValue działa dobrze, podobnie jak 0 until Int.MaxValue. A ta druga jest, co i tak robi pętla while (to zawiera właściwy punkt końcowy, until go pomija).

W każdym razie, ponieważ Scala jest zupełnie innym (o wiele bardziej ogólnym) stworzeniem niż C for, krótka odpowiedź brzmi: nie, nie można zrobić dokładnie tego samego. Ale prawdopodobnie możesz zrobić to, co chcesz, dzięki for (choć może nie tak szybko, jak chcesz, ponieważ istnieje pewna kara za wydajność).

4

Tak i nie, to zależy od tego, o co prosisz. Jeśli pytasz, czy można iteracyjne nad sekwencji liczb całkowitych bez konieczności tworzenia tej sekwencji, potem tak można na przykład za pomocą strumieni:

def fromTo(from : Int, to : Int) : Stream[Int] = 
    if(from > to) { 
    Stream.empty 
    } else { 
    // println("one more.") // uncomment to see when it is called 
    Stream.cons(from, fromTo(from + 1, to)) 
    } 

wówczas:

for(i <- fromTo(0, 5)) println(i) 

pisanie własny iterator definiując hasNext, a następnie jest inną opcją.

Jeśli pytasz, czy możesz użyć składni "for", aby napisać "natywną" pętlę, tj. Pętlę, która działa poprzez inkrementowanie natywnej liczby całkowitej zamiast iterowania wartości tworzonych przez instancję obiektu, to odpowiedź brzmi, o ile wiem, nie. Jak być może wiesz, "dla" rozumienia jest cukier syntaktyczny dla kombinacji wywołań flatMap, filter, map i/lub foreach (wszystkie zdefiniowane w cechach FilterMonadic), w zależności od zagnieżdżenia generatorów i ich typów. Możesz spróbować skompilować trochę pętli i wydrukować jej pośrednią reprezentację kompilatora, aby zobaczyć, jak są one rozwinięte.

+0

Wow, trudna odpowiedź, ale dobra. Po prostu uczę się Scali, więc używałeś wielu terminów, z których jestem zaledwie zaznajomiony, ale dzięki. –

+0

Definicja 'fromTo' może być dodatkowo uproszczona za pomocą metody' iterate' na obiekcie towarzyszącym 'Stream' (lub' Iterator'). Coś w rodzaju: 'def fromTo (from: Int, to: Int) = Stream.iterate (from, to - from) (_ + 1)'. Ale używanie 'od do do' jest bardziej idiomatyczne i osiąga to samo. –

2

Jest ich kilka, ale nie mogę się tym przejmować. Poniżej zamieszczona jest całkiem kanoniczny:

@scala.annotation.tailrec 
def loop(from: Int, until: Int)(f: Int => Unit): Unit = { 
    if (from < until) { 
    f(from) 
    loop(from + 1, until)(f) 
    } 
} 

loop(0, 10) { i => 
    println("Hi " + i) 
} 
5

Wow, kilka ciekawych odpowiedzi techniczne proste pytanie (co jest dobre!), Ale w przypadku gdy ktoś jest po prostu patrząc na prostą odpowiedź:

//start from 0, stop at 9 inclusive 
for (i <- 0 until 10){ 
    println("Hi " + i) 
} 

//or start from 0, stop at 9 inclusive 
for (i <- 0 to 9){ 
    println("Hi " + i) 
} 

Jak Rex podkreślił, „do” obejmuje prawo punkt końcowy, "do" go pomija.

Powiązane problemy