How can I make spans from lengths in Haskell?

Viewed 75

I have a list of string lengths, and I want to make span indices from them. So given lengths [23, 27, 47] I want to produce [(0, 23), (24, 27), (28, 47)]. How can I do that?

Here's what I have so far. I'm not sure I'm on the right track:

makeSpans :: [Int] -> [(Int, Int)]
makeSpans [x:y:xs] = [(x+1, y), makeSpans xs]
makeSpans [x:y] = (x+1, y)
makeSpans y = y

2 Answers

Here's a small example of what you want:

> let list = ["abc", "abc", "abc"]
> import Control.Arrow
> fmap (second pred) $ zip <*> tail $ scanl (\acc x -> acc + length x) 0 list
> [(0,2),(3,5),(6,8)]

Broken down, that is:

  1. scanl the list, adding each word's length to the accumulator
  2. Create pairs of each list item and its successor:
> zip <$> tail $ [0,3,6,9]
> [(0,3),(3,6),(6,9)]
  1. Fix the pairs so that they don't overlap, using second to map on the second element of each pair (pred is equivalent to (-)1).

To me, this type of solution is easier to build up than writing recursive functions from scratch. And as a bonus, it reads like a pipeline, so no need to keep track of multiple levels of indirection and mind-boggling recursion.

Your syntax is a bit off, perhaps you would benefit from some structured learning materials?

You haven't really discussed bad inputs / edge-cases but this might give you a starting point.

makeSpans = go 0
  where go n (x:xs) = (n, x) : go (succ x) xs
        go _ [] = []
Related