Давайте сначала объясним проблему.Этот код:
var j=1
for(i<-1.to(100).by(scala.math.pow(5,j).toInt))
{
println(i+" "+j)
j=j+1
}
эквивалентен этому:
var j = 1
val range: Range = Predef.intWrapper(1).to(100)
val increment: Int = scala.math.pow(5, j).toInt
val byRange: Range = range.by(increment)
byRange.foreach {
println(i+" "+j)
j=j+1
}
Таким образом, к моменту, когда вы мутируете, j
, increment
и byRange
уже вычислены,И Range
является неизменным объектом - вы не можете его изменить.Даже если вы создали новые диапазоны, когда вы сделали foreach
, объект, выполняющий foreach
, все равно остался бы таким же.
Теперь к решению.Проще говоря, Range
не подходит для ваших нужд.Вы хотите геометрическую прогрессию, а не арифметическую.Для меня (и, похоже, в значительной степени для всех остальных, отвечающих на этот вопрос) естественным решением было бы использование Stream
или Iterator
, созданного с помощью iterate
, который вычисляет следующее значение на основе предыдущего.
for(i <- Iterator.iterate(1)(_ * 5) takeWhile (_ < 100)) {
println(i)
}
РЕДАКТИРОВАТЬ: О потоке против итератора
Stream
и Iterator
- это очень разные структуры данных, которые обладают свойством нестрогого .Именно это свойство позволяет iterate
даже существовать, поскольку этот метод создает бесконечную коллекцию 1 , из которой takeWhile
создаст new 2 коллекция, которая является конечной.Давайте посмотрим здесь:
val s1 = Stream.iterate(1)(_ * 5) // s1 is infinite
val s2 = s1.takeWhile(_ < 100) // s2 is finite
val i1 = Iterator.iterate(1)(_ * 5) // i1 is infinite
val i2 = i1.takeWhile(_ < 100) // i2 is finite
Эти бесконечные коллекции возможны, потому что коллекция не предварительно вычислена .На List
все элементы внутри списка фактически хранятся где-то к моменту создания списка.Однако в приведенных выше примерах заранее известен только первый элемент каждой коллекции.Все остальные будут вычисляться только тогда и только тогда, когда потребуется.
Как я уже упоминал, в других отношениях это очень разные коллекции.Stream
- это структура данных immutable
.Например, вы можете печатать содержимое s2
столько раз, сколько пожелаете, и каждый раз будет отображаться один и тот же результат.С другой стороны, Iterator
является изменяемой структурой данных.После того, как вы использовали значение, оно исчезнет навсегда.Напечатайте содержимое i2
дважды, и оно будет пустым во второй раз:
scala> s2 foreach println
1
5
25
scala> s2 foreach println
1
5
25
scala> i2 foreach println
1
5
25
scala> i2 foreach println
scala>
Stream
, с другой стороны, является коллекцией lazy
.Как только значение будет вычислено, оно будет оставаться вычисленным, вместо того, чтобы каждый раз сбрасываться или пересчитываться.Ниже приведен один пример такого поведения в действии:
scala> val s2 = s1.takeWhile(_ < 100) // s2 is finite
s2: scala.collection.immutable.Stream[Int] = Stream(1, ?)
scala> println(s2)
Stream(1, ?)
scala> s2 foreach println
1
5
25
scala> println(s2)
Stream(1, 5, 25)
Так что Stream
может фактически заполнить память, если не соблюдать осторожность, тогда как Iterator
занимает постоянное место.С другой стороны, можно удивиться Iterator
из-за его побочных эффектов.
(1) На самом деле, Iterator
вообще не является коллекцией, даже если она разделяет многие методы, предоставляемые коллекциями.С другой стороны, из описания проблемы, которое вы дали, вы на самом деле не заинтересованы в наборе чисел, просто в их переборе.
(2) На самом деле, хотя takeWhile
создаст новый Iterator
в Scala 2.8.0, этот новый итератор будет по-прежнему связан со старым, и изменения в одном будут иметь побочные эффекты для другого.Это подлежит обсуждению, и в будущем они могут стать действительно независимыми.