Совпадение образов и бесконечные потоки

Итак, я работаю над тем, чтобы научить себя Scala, и одна из вещей, с которыми я играл, это класс Stream . Я попытался использовать наивный перевод классической версии Хаскелла решения Дийкстры к проблеме с номерами Хамминга:

object LazyHammingBad {
  private def merge(a: Stream[BigInt], b: Stream[BigInt]): Stream[BigInt] =
    (a, b) match {
      case (x #:: xs, y #:: ys) =>
        if (x < y) x #:: merge(xs, b)
        else if (y < x) y #:: merge(a, ys)
        else x #:: merge(xs, ys)
    }

  val numbers: Stream[BigInt] =
    1 #:: merge(numbers map { _ * 2 },
      merge(numbers map { _ * 3 }, numbers map { _ * 5 }))
}

Принятие этого за поворот в переводчике быстро привело к разочарованию:

scala> LazyHammingBad.numbers.take(10).toList
java.lang.StackOverflowError

Я решил посмотреть, не решили ли другие люди проблему в Scala, используя подход Хаскелла, и адаптировал это решение из кода Rosetta:

object LazyHammingGood {
  private def merge(a: Stream[BigInt], b: Stream[BigInt]): Stream[BigInt] =
    if (a.head < b.head) a.head #:: merge(a.tail, b)
    else if (b.head < a.head) b.head #:: merge(a, b.tail)
    else a.head #:: merge(a.tail, b.tail)

  val numbers: Stream[BigInt] = 
    1 #:: merge(numbers map {_ * 2}, 
            merge(numbers map {_ * 3}, numbers map {_ * 5}))
}

Это решение работало хорошо, но я все еще удивляюсь, как я ошибся в LazyHammingBad. Неужели использование #:: для разрушения x #:: xs по каким-то причинам заставляет оценивать xs? Есть ли какой-нибудь способ безопасно использовать совпадение паттернов с бесконечными потоками, или вы просто должны использовать голову и хвост , если вы не хотите, чтобы что-то взорвалось?

20
задан Pillsy 21 September 2011 в 22:17
поделиться