我曾在Javaranch上问过这个问题,但在那里得不到答复.所以在这里发布它:
我有这个特殊要求,其中循环变量的增量是通过在每次迭代后将其乘以5来完成的.在Java中,我们可以这样实现它:
for(int i=1;i<100;i=i*5){}
Run Code Online (Sandbox Code Playgroud)
在scala我尝试以下代码 -
var j=1
for(i<-1.to(100).by(scala.math.pow(5,j).toInt))
{
println(i+" "+j)
j=j+1
}
Run Code Online (Sandbox Code Playgroud)
但它的打印输出如下:1 1 6 2 11 3 16 4 21 5 26 6 31 7 36 8 ....
它总是递增5.那么我如何实际将增量乘以5而不是添加它.
Dan*_*ral 18
我们先解释一下这个问题.这段代码:
var j=1
for(i<-1.to(100).by(scala.math.pow(5,j).toInt))
{
println(i+" "+j)
j=j+1
}
Run Code Online (Sandbox Code Playgroud)
相当于:
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
}
Run Code Online (Sandbox Code Playgroud)
所以,你的时间得到变异j,increment并且byRange已经被计算.并且Range是一个不可变的对象 - 你无法改变它.即使您在执行此操作时生成了新范围foreach,执行此操作的对象foreach仍将是相同的.
现在,到解决方案.简而言之,Range不足以满足您的需求.你想要几何级数,而不是算术级数.对我来说(并且几乎所有人都回答,似乎),自然的解决方案是使用a Stream或者Iterator创建with iterate,它根据前一个计算下一个值.
for(i <- Iterator.iterate(1)(_ * 5) takeWhile (_ < 100)) {
println(i)
}
Run Code Online (Sandbox Code Playgroud)
编辑:关于Stream vs Iterator
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
Run Code Online (Sandbox Code Playgroud)
这些无限集合是可能的,因为集合不是预先计算的.在a上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>
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
因此Stream,如果不小心,实际上可以填满内存,而Iterator占用恒定的空间.另一方面,Iterator由于其副作用,人们会感到惊讶.
(1)事实上,Iterator即使它共享集合提供的许多方法,也不是一个集合.另一方面,从你给出的问题描述中,你并没有真正感兴趣的是拥有一组数字,只是在迭代它们.
(2)实际上,虽然takeWhile会Iterator在Scala 2.8.0上创建一个新的,但是这个新的迭代器仍将链接到旧的迭代器,而其中一个的变化对另一个有副作用.这需要讨论,它们最终可能在未来真正独立.
mic*_*ebe 10
在更实用的风格:
scala> Stream.iterate(1)(i => i * 5).takeWhile(i => i < 100).toList
res0: List[Int] = List(1, 5, 25)
Run Code Online (Sandbox Code Playgroud)
并且有更多的语法糖:
scala> Stream.iterate(1)(_ * 5).takeWhile(_ < 100).toList
res1: List[Int] = List(1, 5, 25)
Run Code Online (Sandbox Code Playgroud)