Scala:在修改序列时迭代序列?

时间:2010-12-23 22:02:23

标签: scala functional-programming iterator sieve-of-eratosthenes

我正在尝试在Scala中实现Sieve of Eratosthenes

我首先初始化所有奇数加2的序列:

// (end goal is to find all prime factors of bigNumber)
val largestPrime : Long = Math.ceil(Math.sqrt(bigNumber)).toLong
var nums : Seq[Long] = (3L to largestPrime by 2L).toSeq
nums +: 2L

现在nums包含Seq(2,3,5,7,9,11,13,15,...,(largestPrime))。然后,通过Sieve,我想迭代每个元素,并从Seq中过滤掉该元素的所有倍数。它看起来像这样,除了这只是迭代每个奇数:

for(i : Long <- 3L to largestPrime by 2L) {
    nums = nums.filter((j : Long) => j == i || j % i != 0)
}

相反,我想要使用这样的东西:

for(i <- nums) {
    // filter
}

但是,当然,这只是简单地将序列复制到迭代器中,然后迭代nums中的每个值,就像它在for循环的开头一样(所以在这种情况下,它完全等同于前面的例子) )。我想要它,每次迭代,从nums获取下一个值。

实现这个的最佳方法是什么?我应该使用索引变量和while循环吗?我不确定如何从序列中获取元素(即如何获取序列的元素x,其中x是索引)。或者有更实用的方法吗?


编辑:我刚刚找到scanLeft函数,我正试图掌握如何使用它,因为我怀疑它可能在这种情况下有用......

4 个答案:

答案 0 :(得分:4)

让我们从我认为最重要的问题开始吧。你有这个:

for (i <- mi) { mi = something else }

不会更改正在迭代的mimi始终保持不变。可能您可以变异 mi的值,但更改它将无效。顺便说一下,改变它也可能不起作用。

那么,你是怎么做到的?你不用于理解 - 或者至少不是这种方式。您可以查看我自己的版本here,它会迭代不同于正在变异的集合。或者这是一个单行:

(n: Int) => (2 to n) |> (r => r.foldLeft(r.toSet)((ps, x) => if (ps(x)) ps -- (x * x to n by x) else ps))

现在,回到你想要做什么......当你使用for-comprehension时,你实际上是在调用方法foreachmap或{{ 1}},所以你需要一个能够处理这些方法之一的集合没有“下一个”元素从一次迭代变为下一次迭代的麻烦。正如我所说,我不确定Scala的任何系列是否合适。如果你这样做,你最好使用flatMap循环并自己跟踪事物。例如:

while

请注意,我会指向def primes(n: Int) = { import scala.collection.mutable.LinkedList val primes = LinkedList(3 to n by 2: _*) var p = primes while (p.nonEmpty) { var scanner = p while (scanner.next.nonEmpty) { if (scanner.next.head % p.head == 0) scanner.next = scanner.next.next else scanner = scanner.next } p = p.next } primes } 的开头,通过每个已知素数移动LinkedList,并将p移动到所有剩余数字以剪切非素数。< / p>

答案 1 :(得分:2)

scala.collection.immutable.Stream上的文档中的示例是筛子:

object Main extends Application {

  def from(n: Int): Stream[Int] =
    Stream.cons(n, from(n + 1))

  def sieve(s: Stream[Int]): Stream[Int] =
    Stream.cons(s.head, sieve(s.tail filter { _ % s.head != 0 }))

  def primes = sieve(from(2))

  primes take 10 print
}

答案 2 :(得分:0)

既不是一个好的功能解决方案,也不是一个能够揭示模糊Scala图书馆宝藏的解决方案,但是自己很容易构建一个专门的迭代器。

class ModifyingIterator(var collection: Seq[Long]) extends Iterator[Long] {
  var current = collection.head
  def next = {
    current = collection.find(_ > current).get
    current
  }
  def hasNext = collection.exists(_ > current)
}

val mi = new ModifyingIterator(nums)

for (i <- mi) {
    mi.collection = mi.collection.filter((j : Long) => j == i || j % i != 0)
}
println(mi.collection)

ModifyingIterator跟踪当前项目并允许重新分配用于迭代的集合。下一个项目总是大于当前项目。

当然,应该使用更好的数据结构,它不会跟踪当前的但保留指向当前的指针以便摆脱每次无用的搜索。

答案 3 :(得分:0)

有一篇有趣的论文:http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf

我试图将该论文中给出的Haskell代码翻译成Scala,但我没有测试性能。

object primes {

    type SI = Stream[Int]

    def sieve:SI = {
        def wheel2357:SI = Stream(4,2,4,6,2,6,4,2,4,6,6,
            2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,
            6,4,2,4,6,2,6,4,2,4,2,10,2,10,2) append wheel2357
        def spin(s:SI, n:Int):SI = Stream.cons(n, spin(s.tail, n + s.head))

        case class It(value:Int, step:Int) {
            def next = new It(value + step, step)

            def atLeast(c:Int):It =
            if (value >= c) this
            else new It(value + step, step).atLeast(c)
        }

        implicit object ItOrdering extends Ordering[It] {
            def compare(thiz:It, that:It) = {
                val r = thiz.value - that.value
                if (r == 0) thiz.step - that.step else r
            }

        }

        import scala.collection.immutable.TreeSet

        def sieve(cand:SI, set:Set[It]):SI = {
            val c = cand.head
            val set1 = TreeSet[It]() ++ set.dropWhile(_.value < c) ++
               set.takeWhile(_.value < c).map(_.atLeast(c))
            if (set1.elements.next.value == c) {
                val set2 = TreeSet[It]() ++ set1.dropWhile(_.value == c) ++
                    set1.takeWhile(_.value == c).map(_.next)
                sieve(cand.tail, set2)
            } else {
                Stream.cons(c, sieve(cand.tail, set1 + It(c*c,2*c)))
            }
        }
        Stream(2,3,5,7,11) append sieve(spin(wheel2357,13),
                  new TreeSet[It] + It(121,22))
    }

    def main(args:Array[String]) {
        sieve.takeWhile(_ < 1000).foreach(println)
    }
}