For example:
nLn "This\n\nis an\nexample\n\n" == [2,5,6]
nLn "" == [1]
I have tried this but it doesn't seem to be working, why is that?
nLn :: Integral a => String -> [a]
nLn "" = [1]
nLn (x:xs) = [n | n <- lineBr (x:xs)]
where
lineBr (x:y:xs)
|x == y && y =='\n' = 1
|otherwise = 1 lineBr (y:xs)
lineBr [x] = 0
CodePudding user response:
I have to admit that I don't completely get where you are going with your attempted solution. The reason that it doesn't work, i.e., that is gets rejected by the type checker, is that while your helper function lineBr
returns a single number, it is used by nLn
as if it returns a list, i.e., as the generating expression in a list comprehension.
Here's an implementation (with a simpler type and an arguably more descriptive name) that does the trick:
indicesOfEmptyLines :: String -> [Int]
indicesOfEmptyLines = go 1 True
where
go _ False "" = []
go i True "" = [i]
go i False ('\n' : cs) = go (i 1) True cs
go i True ('\n' : cs) = i : go (i 1) True cs
go i _ (c : cs) = go i False cs
The idea is to have the heavy lifting done by a helper function go
that will iterate over the characters in the input string. The helper function takes two additional arguments: the index of the line that it is currently processing (I followed your example and used 1-based indexing) and a Boolean indicating whether the currently processed line can still be empty.
We consider five cases:
- If we reach the end of the string and the current line is known not to be empty, we know we are done and we have no more indices to report. Hence, we produce the empty list.
- If we reach the end of the string and the current line can still be empty (i.e., we have not encountered any characters on it yet), we know we are done and that the last line was in fact an empty line. Hence, we produce a singleton list containing the index of the last line.
- If we encounter a newline character and the current line is known not to be empty, we advance the index, acknowledge that the next line can still be empty (as we have not encountered any characters on it yet), and we proceed by processing the remainder of the string.
- If we encounter a newline character and the current line can still be empty, then we know that the current line is in fact empty and we prepend the current index to any indices that the recursive call to
go
will produce. We advance the index, acknowledge that the next line can still be empty, and we proceed by processing the remainder of the string. - If we encounter any other character than a newline, we know that the current line cannot be empty (hence, we pass
False
to the recursive call togo
) and we proceed by processing the remainder of the string.
Seeing it in action:
> indicesOfEmptyLines "This\n\nis an\nexample\n\n"
[2,5,6]
> indicesOfEmptyLines ""
[1]