我在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而不是添加它。
答案 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
时,increment
和byRange
已经被计算出来。 Range
是不可变的对象 - 您无法更改它。即使您在执行foreach
时生成了新范围,执行foreach
的对象仍然是相同的。
现在,解决方案。简而言之,Range
不足以满足您的需求。你想要几何级数,而不是算术级数。对我(以及几乎所有其他人回答,似乎),自然的解决方案是使用Stream
或Iterator
创建iterate
,根据前一个计算下一个值之一。
for(i <- Iterator.iterate(1)(_ * 5) takeWhile (_ < 100)) {
println(i)
}
编辑:关于Stream与Iterator
Stream
和Iterator
是非常不同的数据结构,它们共享非严格的属性。此属性使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
上,列表中的所有元素实际上都存储在创建列表的某个位置。然而,在上面的例子中,只预先知道每个集合的第一个元素。所有其他人只会在需要时计算。
正如我所提到的,这些在其他方面是非常不同的集合。 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
将在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的顺序相反。