a match {case x#::xs =>...
примерно равно val (x, xs) = (a.head, a.tail)
.Таким образом, разница между плохой версией и хорошей заключается в том, что в плохой версии вы вызываете a.tail
и b.tail
в самом начале, а не просто используете их для построения хвоста результирующего потока.,Более того, когда вы используете их справа от #::
(не сопоставление с образцом, а построение результата, как в #:: merge(a.b.tail)
, вы фактически не вызываете слияние, что будет сделано только позже, при доступе к хвосту возвращенного потока.Таким образом, в хорошей версии вызов слияния вообще не вызывает * 1007. * В плохой версии он вызывает его сразу при запуске.
Теперь, если вы рассматриваете числа или даже упрощенную версию, скажите1 #:: merge(numbers, anotherStream)
, когда вы звоните, вы звоните tail
по этому (как и take(10)
), необходимо оценить merge
. Вы звоните tail
по numbers
, что вызывает merge
с numbers
какпараметры, который вызывает tails
на numbers
, который вызывает merge
, который вызывает tail
...
В отличие от этого, в супер-ленивом Haskell, когда вы сопоставляете шаблон, он выполняет практически любую работуКогда вы делаете case l of x:xs
, он будет оценивать l
достаточно, чтобы знать, является ли он пустым списком или минусами. Если это действительно минусы, x
и xs
будут доступны как два thunks, функцииэто в конечном итоге даст доступ, позже, к контенту.потерянным эквивалентом в Scala было бы просто проверить empty
.
Обратите внимание, что в Scala Stream, хотя tail
ленив, head
нет.Когда у вас есть (не пустой) поток, голова должна быть известна.Это означает, что когда вы получаете хвост потока, должен быть вычислен сам поток, его заголовок, который является вторым элементом исходного потока.Это иногда проблематично, но в вашем примере вы терпите неудачу, даже не добравшись туда.