Увеличение цикла for (переменная цикла) в scala на мощность 5
Я задал этот вопрос на Javaranch, но не смог получить ответа. Поэтому разместите его здесь:
у меня есть это конкретное требование, где инкремент в переменной цикла должен быть выполнен путем умножения его на 5 после каждой итерации. В Java мы могли бы реализовать его следующим образом:
for(int i=1;i<100;i=i*5){}
в Scala я пробовал следующий код-
var j=1
for(i<-1.to(100).by(scala.math.pow(5,j).toInt))
{
println(i+" "+j)
j=j+1
}
но его печать следующий вывод: 1 1 6 2 11 3 16 4 21 5 26 6 31 7 Тридцать шесть Восемь .... ....
его приращение на 5 всегда. Итак, как я могу фактически умножить приращение на 5 вместо его добавления.
5 ответов
давайте сначала объясним проблему. Этот код:
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)
}
EDIT: о потоке против итератора
Stream
и Iterator
очень разные структуры данных, которые имеют свойство нестрогом. Это свойство является то, что позволяет iterate
даже существовать, так как этот метод создает бесконечную коллекцию1, из которых takeWhile
создать новая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
столько раз, сколько вы хотите, и он будет показывать тот же результат каждый раз. С другой стороны,--14--> - это изменяемые данные структура. Как только вы используете значение, это значение навсегда исчезнет. Распечатать содержимое 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 этот новый итератор по-прежнему будет связан со старым, а изменения в одном имеют побочные эффекты на другом. Это подлежит обсуждению, и в будущем они могут стать действительно независимыми.
в более функциональном стиле:
scala> Stream.iterate(1)(i => i * 5).takeWhile(i => i < 100).toList
res0: List[Int] = List(1, 5, 25)
и с более синтаксическим сахаром:
scala> Stream.iterate(1)(_ * 5).takeWhile(_ < 100).toList
res1: List[Int] = List(1, 5, 25)
может быть, простой цикл while подойдет?
var i=1;
while (i < 100)
{
println(i);
i*=5;
}
или если вы хотите также печати количество итераций
var i=1;
var j=1;
while (i < 100)
{
println(j + " : " + i);
i*=5;
j+=1;
}
похоже, вам, ребята, нравится функционал, так как насчет рекурсивное решение?
@tailrec def quints(n:Int): Unit = {
println(n);
if (n*5<100) quints(n*5);
}
обновление: Спасибо за обнаружение ошибки... конечно, это должна быть власть, а не умножение:
досадно, кажется, нет целого числа pow
функция в стандартной библиотеке!
попробуйте это:
def pow5(i:Int) = math.pow(5,i).toInt
Iterator from 1 map pow5 takeWhile (100>=) toList
или если вы хотите использовать его на месте:
Iterator from 1 map pow5 takeWhile (100>=) foreach {
j => println("number:" + j)
}
и с индексами:
val iter = Iterator from 1 map pow5 takeWhile (100>=)
iter.zipWithIndex foreach { case (j, i) => println(i + " = " + j) }
(0 to 2).map (math.pow (5, _).toInt).zipWithIndex
res25: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,0), (5,1), (25,2))
создает вектор с i, j в обратном порядке.