在Scala中如何使用for循环而不需要序列?

11

所以,在阅读《Scala for the Impatient》时,我想知道:是否可以在没有序列的情况下使用Scala的for循环?

例如,书中有一个练习要求您构建一个计数器对象,该对象不能超过Integer.MAX_VALUE。 为了测试我的解决方案,我编写了以下代码:

var c = new Counter
for( i <- 0 to Integer.MAX_VALUE ) c.increment()

这会抛出一个错误:序列不能包含超过Int.MaxValue个元素。我认为这意味着Scala首先要分配并填充一个序列对象,包含从0到Integer.MaxValue的值,然后在该序列对象上执行foreach循环。

我意识到我可以使用以下方法代替:

var c = new Counter
while(c.value < Integer.MAX_VALUE ) c.increment()

但是有没有办法使用for语句来执行传统的C风格for循环呢?

4个回答

19

事实上,0到N并不会真正地用整数从0N填充任何内容。它创建的是scala.collection.immutable.Range的一个实例,该实例将其方法应用于动态生成的所有整数。

你遇到的错误仅是因为你必须能够将元素数量(无论它们是否实际存在)适配到Int的正部分中,以维护length方法的约定。 1到Int.MaxValue可以正常工作,0 until Int.MaxValue也是如此。而且后者正是你的while循环正在做的事情(to包括右端点,until则排除它)。

总之,由于Scala的for与C的for非常不同(更加通用),所以简短的答案是否定的,你不能完全做到相同的事情。但是,你可能可以使用for做你想要的事情(虽然可能不像你想的那么快,因为会有一些性能损失)。


9

哇,对于一个简单的问题来说,这里有一些不错的技术回答(这很好!)但是如果有人只是想要一个简单的答案:

//start from 0, stop at 9 inclusive
for (i <- 0 until 10){
    println("Hi " + i)
}

//or start from 0, stop at 9 inclusive
for (i <- 0 to 9){
    println("Hi " + i)
}

正如Rex所指出的,"to"包括右端点,而"until"则省略它。

4

是的和不是的,这取决于你所要求的内容。如果你想知道是否可以在不必先构建序列的情况下迭代一组整数,那么是的,例如可以使用流:

def fromTo(from : Int, to : Int) : Stream[Int] = 
  if(from > to) {
    Stream.empty
  } else {
    // println("one more.") // uncomment to see when it is called
    Stream.cons(from, fromTo(from + 1, to))
  }

然后:

for(i <- fromTo(0, 5)) println(i)

通过定义hasNext和next来编写自己的迭代器是另一种选择。

如果你想知道是否可以使用'for'语法编写“原生”的循环,即通过增加某个本地整数而不是迭代由对象实例产生的值来工作的循环,那么据我所知,答案是否定的。正如你所知,'for'推导式是对flatMap、filter、map和/或foreach(所有这些都在FilterMonadic特质中定义)的组合的语法糖,具体取决于生成器的嵌套和它们的类型。你可以尝试编译一些循环并打印其编译器中间表示。

scalac -Xprint:refchecks

查看它们如何展开。


哇,这是一个具有挑战性的答案,但很好。我正在学习Scala,所以你使用了很多我只是略微熟悉的术语,但还是谢谢。 - Adam Ness
fromTo的定义可以通过使用Stream(或Iterator)伴生对象上的方法iterate进一步简化。类似于: def fromTo(from: Int, to: Int) = Stream.iterate(from, to - from)(_ + 1)。但是使用from until to更符合惯用语,并且实现同样的功能。 - Kipton Barros

2

有很多这样的内容,但我现在不想去搜索它们。以下是比较经典的:

@scala.annotation.tailrec
def loop(from: Int, until: Int)(f: Int => Unit): Unit = {
  if (from < until) {
    f(from)
    loop(from + 1, until)(f)
  }
}

loop(0, 10) { i =>
  println("Hi " + i)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接