Scala: повторять последовательность при ее изменении?

Я пытаюсь реализовать Решето Эратосфена в Scala.

Я начинаю с инициализации последовательности всех нечетных чисел плюс 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) ). Затем с помощью решета я хочу перебрать каждый элемент и отфильтровать все кратные этому элементу из 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, я пытаюсь понять, как ее использовать, поскольку я подозреваю, что она может быть полезна в этом случае...


person Ricket    schedule 23.12.2010    source источник
comment
Чтобы сделать это на чисто функциональном языке, вы должны использовать рекурсию функций.   -  person intrepidis    schedule 08.05.2016


Ответы (4)


Давайте начнем с того, что я считаю самой большой проблемой выше. У вас есть это:

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

Это не изменит повторяющийся mi. Это mi останется неизменным. Возможно, вы можете мутировать значение mi, но изменить его не получится. Кстати, его мутация тоже может не сработать.

Итак, как вы это делаете? Вы не используете для понимания - или, по крайней мере, не таким образом. Вы можете посмотреть мою собственную версию здесь, которая перебирает коллекцию, отличную от изменяемой. Или вот однострочный:

(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, вы на самом деле вызываете для него метод foreach, map или flatMap, поэтому вам понадобится коллекция, которая способен обрабатывать один из этих методов и не иметь проблем с "следующим" элементом, изменяющимся от одной итерации к другой. Как я уже сказал, я не уверен, что какая-либо коллекция Scala отвечает всем требованиям. Вам лучше использовать цикл 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 по всем известным простым числам и перемещаю scanner по всем оставшимся числам, чтобы отрезать непростые числа.

person Daniel C. Sobral    schedule 24.12.2010

Пример в документации по 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
}
person Stephen Denne    schedule 23.12.2010
comment
Хотя это ужасно неэффективно. Я попытался изучить scala, создав более эффективную версию, и написал об этом в блоге datacute.wordpress.com/2008/04/16/basic-primes-in-scala - person Stephen Denne; 24.12.2010
comment
Это НЕ Решето Эратосфена (которое вы не утверждаете, но вопрос касается Решета Эратосфена). - person Daniel C. Sobral; 24.12.2010
comment
@Daniel спасибо за ваш ответ, особенно за ссылку на вашу версию, которая очень четко реализует процесс Эратосфена. - person Stephen Denne; 24.12.2010
comment
на самом деле один лайнер также реализует Эратосфена. - person Daniel C. Sobral; 24.12.2010

Ни хорошее функциональное решение, ни решение, которое раскрывает неизвестные сокровища библиотеки 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 отслеживает текущий элемент и позволяет переназначить коллекцию, которая используется для итерации. Следующий элемент всегда больше текущего.

Конечно, вероятно, следует использовать лучшую структуру данных, которая не отслеживает текущее значение, а сохраняет указатель на текущий элемент, чтобы избавиться от бесполезной информации. искать каждый раз.

person Debilski    schedule 23.12.2010
comment
Ой, вы продолжаете сканировать Seq снова и снова! - person Daniel C. Sobral; 24.12.2010
comment
Как я уже сказал, это определенно должно быть улучшено. :) - person Debilski; 24.12.2010

Есть интересный документ: 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)
    }
}
person Landei    schedule 24.12.2010