在scala中以5的幂递增for循环(循环变量)

时间:2010-11-16 12:25:02

标签: scala loops

我在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 36 8 .... ....

总是递增5。那么我如何实际将增量乘以5而不是添加它。

5 个答案:

答案 0 :(得分:18)

我们首先解释一下这个问题。这段代码:

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时,incrementbyRange已经被计算出来。 Range不可变的对象 - 您无法更改它。即使您在执行foreach时生成了新范围,执行foreach的对象仍然是相同的。

现在,解决方案。简而言之,Range不足以满足您的需求。你想要几何级数,而不是算术级数。对我(以及几乎所有其他人回答,似乎),自然的解决方案是使用StreamIterator创建iterate,根据前一个计算下一个值之一。

for(i <- Iterator.iterate(1)(_ * 5) takeWhile (_ < 100)) {
  println(i)
}

编辑:关于Stream与Iterator

StreamIterator是非常不同的数据结构,它们共享非严格的属性。此属性使iterate甚至存在,因为此方法创建了一个无限集合 1 takeWhile将从中创建 new < sup> 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上,列表中的所有元素实际上都存储在创建列表的某个位置。然而,在上面的例子中,只预先知道每个集合的第一个元素。所有其他人只会在需要时计算。

正如我所提到的,这些在其他方面是非常不同的集合。 Streamimmutable数据结构。例如,您可以根据需要多次打印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> 
另一方面,

Streamlazy集合。一旦计算了一个值,它将保持计算,而不是每次都被丢弃或重新计算。请参阅下面的一个行为示例:

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将在Scala 2.8.0上创建一个新的Iterator,但这个新的迭代器仍然会链接到旧的迭代器,而其中一个的更改会对其他。这需要讨论,它们最终可能在未来真正独立。

答案 1 :(得分:10)

更具功能性的风格:

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)

答案 2 :(得分:3)

也许一个简单的while循环可以吗?

var i=1;
while (i < 100)
{
   println(i);
   i*=5;
}

或者如果您还希望print迭代次数

var i=1;
var j=1;
while (i < 100)
{
   println(j + " : " + i);
   i*=5;
   j+=1;
}

似乎你们都喜欢功能,那么recursive solution怎么样?

@tailrec def quints(n:Int): Unit = {
  println(n);
  if (n*5<100) quints(n*5);
}

答案 3 :(得分:1)

更新:感谢您发现错误......它当然应该是力量,而不是倍增:

令人讨厌的是,标准库中似乎没有整数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) }

答案 4 :(得分:0)

(0 to 2).map (math.pow (5, _).toInt).zipWithIndex
res25: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,0), (5,1), (25,2))

生成一个Vector,其中i,j的顺序相反。