Увеличение цикла for (переменная цикла) в scala на 5 - PullRequest
7 голосов
/ 16 ноября 2010

Я задавал этот вопрос на Джаваранх , но не смог получить ответ там.Итак, разместив его здесь также:

У меня есть это конкретное требование, в котором приращение переменной цикла должно выполняться путем умножения его на 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 164 21 5 26 6 31 7 36 8 .... ....

Его увеличение на 5 всегда.Так как же мне получить умножение приращения на 5 вместо его добавления?

Ответы [ 5 ]

18 голосов
/ 16 ноября 2010

Давайте сначала объясним проблему.Этот код:

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, этот новый итератор будет по-прежнему связан со старым, и изменения в одном будут иметь побочные эффекты для другого.Это подлежит обсуждению, и в будущем они могут стать действительно независимыми.

10 голосов
/ 16 ноября 2010

В более функциональном стиле:

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)
3 голосов
/ 16 ноября 2010

Может быть, подойдет простой цикл 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);
}
1 голос
/ 16 ноября 2010

Обновление : Спасибо за обнаружение ошибки ... Конечно, это должна быть мощность, а не умножение:

Досадно, что не существует целочисленная 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 голосов
/ 14 сентября 2011
(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 в обратном порядке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...