Incrementing 'i' in scala for loop by diff

2019-06-03 15:49发布

问题:

I want to write a for loop in scala, but the counter should get incremented by more than one (the amount is variable) in some special cases.

回答1:

You can do this with a combination of a filter and an external var. Here is an example:

var nextValidVal = 0
for (i <- 0 to 99; if i >= nextValidVal) {
  var amountToSkip = 0
  // Whatever this loop is for
  nextValidVal = if (amountToSkip > 0) i + amountToSkip + 1 else nextValidVal
}

So in the main body of your loop, you can set amountToSkip to n according to your conditions. The next n values of i´s sequence will be skipped.

If your sequence is pulled from some other kind of sequence, you could do it like this

var skip = 0
for (o <- someCollection if { val res = skip == 0; skip = if (!res) skip - 1 else 0; res } ) {
  // Do stuff
}

If you set skip to a positive value in the body of the loop, the next n elements of the sequence will be skipped.

Of course, this is terribly imperative and side-effecty. I would look for other ways to to this where ever possible, by mapping or filtering or folding the original sequence.



回答2:

You could implement your own stream to reflect step, for example:

import scala.collection.immutable.Stream
import ForStream._
object Test {
  def main(args: Array[String]): Unit = {
    val range = 0 to 20 by 1 withVariableStep; // in case you like definition through range
    //val range = ForStream(0,20,1) // direct definition
    for (i<- range) {
      println(s"i=$i")
      range.step = range.step + 1
    }
  }
}

object ForStream{
  implicit def toForStream(range: Range): ForStream = new ForStreamMaster(range.start, range.end,range.step)
  def apply(head:Int, end:Int, step:Int) = new ForStreamMaster(head, end,step)
}

abstract class ForStream(override val head: Int, val end: Int, var step: Int) extends Stream[Int] {
  override val tailDefined = false
  override val isEmpty = head > end
  def withVariableStep = this
}

class ForStreamMaster(_head: Int, _end: Int, _Step: Int) extends ForStream(_head, _end,_Step){
  override def tail = if (isEmpty) Stream.Empty else new ForStreamSlave(head + step, end, step, this)
}

class ForStreamSlave(_head: Int, _end: Int, _step: Int, val master: ForStream) extends ForStream(_head, _end,_step){
  override def tail = if (isEmpty) Stream.Empty else new ForStreamSlave(head + master.step, end, master.step, master)
}

This prints:

i=0

i=2

i=5

i=9

i=14

i=20

You can define ForStream from Range with implicits, or define it directly. But be carefull:

  1. You are not iterating Range anymore!
  2. Stream should be immutable, but step is mutable!

Also as @om-nom-nom noted, this might be better implemented with recursion



回答3:

Why not use the do-while loop?

var x = 0;
do{
...something
if(condition){change x to something else}
else{something else}
x+=1
}while(some condition for x)