Reputation: 33
http://www.seas.upenn.edu/~cis194/spring13/hw/06-laziness.pdf
The question is about representing the ruler function
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, . . . where the nth element in the stream (assuming the first element corresponds to n = 1) is the largest power of 2 which evenly divides n
I have working solution using show to display the first 20 elements with interleaveStreams' function:
data Stream a = Cons a (Stream a)
streamToList :: Stream a -> [a]
streamToList (Cons a b) = a : streamToList b
instance Show a => Show (Stream a) where
show = show . take 20 . streamToList
streamRepeat :: a -> Stream a
streamRepeat a = Cons a (streamRepeat a)
ruler :: Stream Integer
ruler =
let s n = interleaveStreams' (streamRepeat n) (s (n+1))
in s 0
interleaveStreams :: Stream a -> Stream a -> Stream a
interleaveStreams (Cons x xs) (Cons y ys) = Cons x (Cons y (interleaveStreams xs ys))
interleaveStreams' :: Stream a -> Stream a -> Stream a
interleaveStreams' (Cons x xs) y = Cons x $ interleaveStreams' y xs
However I don't understand why ruler using the interleave function instead does not terminate with Show.
Upvotes: 2
Views: 245
Reputation: 52280
first congrats - you solved the hard part ;)
To your question: if you use the interleaveStreams
than it will pattern match on the second Cons
too - but if you look at your code you see that the second part is generated by:
let s n = interleaveStreams ... (s (n+1))
so if interleaveStreams
now asks it to produce a Cons
for this part too you end up in an infinite loop
The other function solves this issue by only forcing the first constructor, which you get at once fromstreamRepeat
s 0
= interleaveStreams (streamRepeat 0) (s 1))
{ need both cons }
= interleaveStreams (Cons 0 (streamRepeat 0)) (s 1)
= interleaveStreams (Cons ...) (interleaveStreams (streamRepeat 1) (s 2))
{ the inner interleaveStream needs both Cons again for it's pattern }
= ...
you'll never get to a Cons
and streamToList
will never be able to produce a list-cons and then you have your problem
s 0
= interleaveStreams' (streamRepeat 0) (s 1))
{ need only first Cons for the pattern-match }
= interleaveStreams' (Cons 0 (streamRepeat 0)) (s 1)
= Cons 0 $ interleaveStreams' (s 1) (streamRepeat 0)
= ...
as you can see you get the Cons
which is the happy path for laziness in show
/streamToList
by the way: you can write this without the s
inner-function by using streamMap
:
ruler :: Stream Integer
ruler = interleaveStreams (streamRepeat 0) (streamMap (+1) ruler)
Upvotes: 2