0

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
  • 1
    a minor point perhaps, but `[n | n <- lineBr (x:xs)]` is entirely equivalent to `lineBr (x:xs)`. Which in turn means you can completely eliminate your `where` clause and just work with top-level pattern matching of the argument. – Robin Zigmond Nov 08 '21 at 17:26
  • As I understand `"This\n\nis an\nexample\n\n"` must have `[2,5,6]` as result since there are 2 empty lines at the end. Is that not so? – user1984 Nov 08 '21 at 17:47
  • Yes,sorry I did not see it – helpmeplease Nov 08 '21 at 17:50
  • also, an empty string `""` is not an empty line. An empty line is `"\n"`. Otherwise we would count `"\n"` as 2 empty lines. Can you check that too? – user1984 Nov 08 '21 at 17:52
  • Here's a hint. You expect `lineBr` to return a list, right? Say, `[3, 7]` or something. So what does `1+ lineBr (y:xs)` -- which might evaluate to `1+ [3,7]`, say -- mean? – Daniel Wagner Nov 08 '21 at 18:09
  • @user1984, if you say that `"This\n\nis an\nexample\n\n"` has 3 empty lines, that means that the final `\n` is between two empty lines, which in turn means that `""` is 1 empty line whereas `"\n"` is 2 empty lines. There's a contraddiction. However, whether one or the other is the truth, depends on [the convention](https://stackoverflow.com/a/67337745/5825294). – Enlico Nov 08 '21 at 18:53
  • @Enlico, an empty line is a `\n` that is at the end of the string or is followed by another `\n`. Running `λ> putStrLn "This\n\nis an\nexample\n\n"` in `ghci` shows that visually. You could make a definition that differs but that's not the convention. – user1984 Nov 08 '21 at 19:04
  • I expect lineBr to return an integral not a list – helpmeplease Nov 08 '21 at 19:08
  • @user1984, _`"This\n\nis an\nexample\n\n"` must have `[2,5,6]` as result since there are 2 empty lines at the end. Is that not so?_ No, `length $ filter null $ lines "This\n\nis an\nexample\n\n"` gives 2 (one between the first two `\n`s, and one between the last two). I hope this clarifies my previous comment. – Enlico Nov 08 '21 at 19:16
  • this is different, you are splitting by lines. It doesn't show the number of empty lines. But this isn't the place for such an argument. Peace. – user1984 Nov 08 '21 at 19:25

1 Answers1

0

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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 to go) 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]
Stefan Holdermans
  • 7,990
  • 1
  • 26
  • 31