A library-based solution:
When faced with this sort of Haskell job, it is often a good idea to try to find a suitable library function for it. Unlike ad hoc manual recursion, an official library function can be trusted to interact smoothly with the idiosyncrasies of the Haskell runtime system.
Here, the job at hand seems well suited to the unfoldr
library function:
unfoldr :: (st -> Maybe (a, st)) -> st -> [a]
The unfoldr
function takes a) an initial state and b) a state transition function, which produces (maybe) a new state together with an output value.
Here, the state could consist of a triplet containing:
- the depth into the initial list of values
- the remaining part of the list of indices
- the remaining part of the list of values
As a preliminary step, we assume temporarily that the list of indices is ordered, so we can process both lists in linear fashion (this restriction will get lifted further below).
This gives the following code:
import Data.List (unfoldr)
search1 :: [Int] -> [a] -> [a]
search1 indices xs = unfoldr stepFn state0
where
state0 = (0, indices, xs)
stepFn (_, [], _) = Nothing -- no indices left, hence the end
stepFn (d, idx:idxs, xs) = let
ofs = idx - d
rest = drop ofs xs
in
Just ( head rest,
(idx, idxs, rest) )
Testing:
$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/ :? for help
λ>
λ> :load q72853867.hs
[1 of 1] Compiling Main ( q72853867.hs, interpreted )
Ok, one module loaded.
λ>
λ> search1 [0,0,2,5,5,9,12] [0..12]
[0,0,2,5,5,9,12]
λ>
So that works. Now looking to lift the ordered indices restriction.
Allowing for unordered lists of indices:
The idea here is to sort the list of indices, but to do so in a way that keeps track of the rank occupied in the initial list by any given index. The idea is mentioned in a comment by @leftaroundabout
.
So we can write a search2p
auxiliary function which produces the requested values, but paired with their corresponding index ranks. Next, we provide a search2
wrapper function that restores the initial index order and discards the rank information:
import Data.List (unfoldr, sortBy)
import Data.Ord (comparing)
search2p :: [Int] -> [a] -> [(Int,a)]
search2p indices xs = unfoldr stepFn state0
where
state0 = (0, sortBy (comparing fst) (zip indices [(0::Int)..]), xs)
stepFn (_, [], _) = Nothing -- no indices left, hence the end
stepFn (d, (idx, rank):idxs, xs) = let
ofs = idx - d
rest = drop ofs xs
in
Just ( (rank, head rest) ,
(idx, idxs, rest) )
search2 :: [Int] -> [a] -> [a]
search2 indices xs = map snd $ sortBy (comparing fst) $ search2p indices xs
Note: this is similar to a technique known in some circles as the decorate-sort-undecorate paradigm.
Testing:
λ>
λ> search2p [0,3,0,5,12,1,1] [0..12]
[(0,0),(2,0),(5,1),(6,1),(1,3),(3,5),(4,12)]
λ>
λ> search2 [0,3,0,5,12,1,1] [0..12]
[0,3,0,5,12,1,1]
λ>