2011-09-08 7 views
9

Così, mentre mi sto muovendo attraverso "Scala per gli impazienti" mi sono chiesto: puoi usare un ciclo di Scala for senza sequenza?Per ciclo in scala senza sequenza?

Ad esempio, esiste un esercizio nel libro che richiede di creare un oggetto contatore che non può essere incrementato oltre Integer.MAX_VALUE. Al fine di testare la mia soluzione, ho scritto il seguente codice:

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

Questo genera un errore: le sequenze non possono contenere più di elementi Int.MaxValue. Mi sembra che Scala stia inizialmente assegnando e popolando un oggetto sequenza, con i valori da 0 a Integer.MaxValue, e quindi facendo un ciclo foreach su quell'oggetto sequenza.

mi rendo conto che avrei potuto fare questo, invece:

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

Ma esiste un modo per fare un tradizionale stile C per ciclo con l'istruzione for?

risposta

17

Infatti, 0 to N in realtà non popolano nulla con numeri interi 0-N. Crea invece un'istanza di scala.collection.immutable.Range, che applica i suoi metodi a tutti gli interi generati al volo.

L'errore è stato eseguito in è solo perché devi essere in grado di adattare il numero di elementi (se effettivamente esistano o meno) nella parte positiva di un Int al fine di mantenere il contratto per il metodo length. 1 to Int.MaxValue funziona bene, così come lo 0 until Int.MaxValue. E quest'ultimo è ciò che il tuo ciclo while sta facendo comunque (to include l'endpoint giusto, until lo omette).

In ogni caso, poiché Scala for è una creatura molto diversa (molto più generica) rispetto a C for, la risposta breve è no, non è possibile fare esattamente la stessa cosa. Ma puoi probabilmente fare quello che vuoi con for (anche se forse non è veloce come vuoi, dato che c'è qualche penalità sulle prestazioni).

4

Sì e no, dipende da cosa si sta chiedendo. Se stai chiedendo se è possibile iterare su una sequenza di interi senza dover costruire quella sequenza, poi sì, si può, ad esempio, utilizzando i flussi:

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)) 
    } 

Poi:

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

Scrivere il tuo proprio iteratore definendo hasNext e next è un'altra opzione.

Se si sta chiedendo se è possibile utilizzare la sintassi 'for' per scrivere un ciclo "nativo", ovvero un ciclo che funziona incrementando alcuni numeri interi nativi piuttosto che iterando su valori prodotti da un'istanza di un oggetto, quindi la risposta è, per quanto ne so, no. Come forse saprete, le "per" intese sono zucchero sintattico per una combinazione di chiamate a flatMap, filtro, mappa e/o foreach (tutte definite nel tratto FilterMonadic), a seconda del nidificazione di generatori e dei loro tipi. Puoi provare a compilare un ciclo e stampare la sua rappresentazione intermedia del compilatore con

scalac -Xprint:refchecks 

per vedere come sono espansi.

+0

Wow, una risposta impegnativa, ma buona. Sto solo imparando Scala, quindi hai usato un sacco di termini con cui ho a malapena familiarità, ma grazie. –

+0

La definizione di 'fromTo' può essere ulteriormente semplificata utilizzando il metodo' iterate' sull'oggetto companion 'Stream' (o' Iterator'). Qualcosa sulla falsariga di: 'def fromTo (from: Int, to: Int) = Stream.iterate (from, to - from) (_ + 1)'. Ma usare 'from until to' è più idiomatico e raggiunge la stessa cosa. –

2

C'è un sacco di questi là fuori, ma non posso essere disturbato a cercarli su Google al momento. Quanto segue è abbastanza canonico:

@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, alcune risposte tecniche belle per una semplice domanda (che è buono!) Ma nel caso qualcuno è solo in cerca di una risposta semplice:

//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) 
} 

come Rex ha sottolineato, "a" include il diritto endpoint, "until" lo omette.

Problemi correlati