将 scala 中的 for 循环(循环变量)增加 5 次方

发布于 2024-10-02 12:42:41 字数 537 浏览 3 评论 0 原文

我曾在 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 而不是相加呢?

I had asked this question on Javaranch, but couldn't get a response there. So posting it here as well:

I have this particular requirement where the increment in the loop variable is to be done by multiplying it with 5 after each iteration. In Java we could implement it this way:

for(int i=1;i<100;i=i*5){}

In scala I was trying the following code-

var j=1
for(i<-1.to(100).by(scala.math.pow(5,j).toInt))
{
  println(i+" "+j)
  j=j+1
}

But its printing the following output:
1 1
6 2
11 3
16 4
21 5
26 6
31 7
36 8
....
....

Its incrementing by 5 always. So how do I got about actually multiplying the increment by 5 instead of adding it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

弱骨蛰伏 2024-10-09 12:42:41

我们先来解释一下问题。此代码:

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 不足以满足您的需求。你想要的是几何级数,而不是算术级数。对我来说(似乎几乎所有其他人都在回答),自然的解决方案是使用使用 iterate 创建的 StreamIterator,它根据前一个值计算下一个值。

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

编辑:关于 Stream 与 Iterator

StreamIterator 是非常不同的数据结构,它们共享非严格的属性嗯>。此属性使 iterate 甚至能够存在,因为此方法正在创建一个无限集合1takeWhile 将从中创建一个 new2 集合是有限的。让我们看看这里:

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 是一种不可变数据结构。例如,您可以根据需要多次打印 s2 的内容,并且每次都会显示相同的输出。另一方面,迭代器是一种可变数据结构。一旦你使用了一个值,这个值就会永远消失。打印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,但这个新的迭代器仍然会链接到旧的迭代器,并且其中的变化是有副作用的对对方的影响。这还有待讨论,他们将来可能最终会真正独立。

Let's first explain the problem. This code:

var j=1
for(i<-1.to(100).by(scala.math.pow(5,j).toInt))
{
  println(i+" "+j)
  j=j+1
}

is equivalent to this:

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
}

So, by the time you get to mutate j, increment and byRange have already been computed. And Range is an immutable object -- you can't change it. Even if you produced new ranges while you did the foreach, the object doing the foreach would still be the same.

Now, to the solution. Simply put, Range is not adequate for your needs. You want a geometric progression, not an arithmetic one. To me (and pretty much everyone else answering, it seems), the natural solution would be to use a Stream or Iterator created with iterate, which computes the next value based on the previous one.

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

EDIT: About Stream vs Iterator

Stream and Iterator are very different data structures, that share the property of being non-strict. This property is what enables iterate to even exist, since this method is creating an infinite collection1, from which takeWhile will create a new2 collection which is finite. Let's see here:

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

These infinite collections are possible because the collection is not pre-computed. On a List, all elements inside the list are actually stored somewhere by the time the list has been created. On the above examples, however, only the first element of each collection is known in advance. All others will only be computed if and when required.

As I mentioned, though, these are very different collections in other respects. Stream is an immutable data structure. For instance, you can print the contents of s2 as many times as you wish, and it will show the same output every time. On the other hand, Iterator is a mutable data structure. Once you used a value, that value will be forever gone. Print the contents of i2 twice, and it will be empty the second time around:

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, on the other hand, is a lazy collection. Once a value has been computed, it will stay computed, instead of being discarded or recomputed every time. See below one example of that behavior in action:

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)

So Stream can actually fill up the memory if one is not careful, whereas Iterator occupies constant space. On the other hand, one can be surprised by Iterator, because of its side effects.

(1) As a matter of fact, Iterator is not a collection at all, even though it shares a lot of the methods provided by collections. On the other hand, from the problem description you gave, you are not really interested in having a collection of numbers, just in iterating through them.

(2) Actually, though takeWhile will create a new Iterator on Scala 2.8.0, this new iterator will still be linked to the old one, and changes in one have side effects on the other. This is subject to discussion, and they might end up being truly independent in the future.

掩耳倾听 2024-10-09 12:42:41

以更实用的风格:

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)

In a more functional style:

scala> Stream.iterate(1)(i => i * 5).takeWhile(i => i < 100).toList
res0: List[Int] = List(1, 5, 25)

And with more syntactic sugar:

scala> Stream.iterate(1)(_ * 5).takeWhile(_ < 100).toList
res1: List[Int] = List(1, 5, 25)
爱要勇敢去追 2024-10-09 12:42:41

也许一个简单的 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);
}

Maybe a simple while-loop would do?

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

or if you want to also print the number of iterations

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

it seems you guys likes functional so how about a recursive solution?

@tailrec def quints(n:Int): Unit = {
  println(n);
  if (n*5<100) quints(n*5);
}
捶死心动 2024-10-09 12:42:41

更新:感谢您发现错误...它当然应该是幂,而不是乘法:

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

Update: Thanks for spotting the error... it should of course be power, not multiply:

Annoyingly, there doesn't seem to be an integer pow function in the standard library!

Try this:

def pow5(i:Int) = math.pow(5,i).toInt
Iterator from 1 map pow5 takeWhile (100>=) toList

Or if you want to use it in-place:

Iterator from 1 map pow5 takeWhile (100>=) foreach {
  j => println("number:" + j)
}

and with the indices:

val iter = Iterator from 1 map pow5 takeWhile (100>=)
iter.zipWithIndex foreach { case (j, i) => println(i + " = " + j) }
迷荒 2024-10-09 12:42:41
(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 的顺序相反。

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

produces a Vector, with i,j in reversed order.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文