Итак, я работаю над тем, чтобы научить себя 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
? Есть ли какой-нибудь способ безопасно использовать совпадение паттернов с бесконечными потоками, или вы просто должны использовать голову
и хвост
, если вы не хотите, чтобы что-то взорвалось?