Итак, я работаю, чтобы научить себя Scala, и одна из вещей, с которыми я играл, - это класс Stream
. Я попытался использовать наивный перевод классической версии Dikkstra версии Haskell для проблемы номера Хэмминга:
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 с использованием подхода Haskell и адаптировали это решение из кода 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
? Есть ли способ безопасно использовать сопоставление шаблонов с бесконечными потоками, или вам просто нужно использовать head
и tail
, если вы не хотите, чтобы что-то взорвалось?