I'm currently working on an implementation of Project Euler problem 40 and am trying to figure out how to take multiple items from a list in Haskell without starting the list over.
Currently, I have a list champernowne
of type Integral a => [a]
which will return an infinite list of the digits of Champernowne's constant, then I take the first, tenth, etc. terms off of this sequence and multiply them to get the answer. The actual code for this is:
ans = (product . map (champernowne !!)) [0, 9, 99, 999, 9999, 99999]
The problem with this implementation is (I'm assuming) that Haskell will go through the list from the beginning of the sequence each time that it wants to get a new term. How can I make it so that haskell only goes through the sequence from elements 1 to 1 000 000 and then pulls the terms out of the middle? I've already tried scanl in hope that the lazy evaluation would aid me, but it did not:
ans = (product . head . scanl (flip drop) champernowne) [10, 90, 900, 9000, 90000]
Just to clarify, the first bit of code does work, but I'm trying to improve my implementation to be a bit more efficient.
The efficient method to solve that problem is to compute the digits without constructing the list.
But if your desired indices are given in ascending order, you can get that without starting from the fron for each by computing the relative offsets and drop
ping the appropriate number of items to reach the next desired index
-- supposes the list of indices in ascending order
indices :: [a] -> [Int] -> [a]
indices xs is = go xs offsets
where
offsets = zipWith (-) is (0:is)
go ys (k:ks) = case drop k ys of
z:zs -> z : go (z:zs) ks
_ -> []
go _ [] = []
you just have a slight error in your second expression: use map head
instead of head
there.
map (list !!) xs -- [0, 9, 99, 999, 9999, 99999] ==
map head . tail . scanl (flip drop) list $ zipWith (-) xs (0:xs)
-- [9, 90, 900, 9000, 90000]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With