Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell — Get multiple values from infinite list without starting the list over

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.

like image 878
Undeterminant Avatar asked Feb 19 '23 01:02

Undeterminant


2 Answers

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 dropping 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 _ [] = []
like image 199
Daniel Fischer Avatar answered Apr 30 '23 21:04

Daniel Fischer


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]
like image 43
arrrghidian Avatar answered Apr 30 '23 23:04

arrrghidian