Увеличение цикла 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 в обратном порядке.