Reputation: 409
I am very new to Haskell and am trying a simple exercise for practice. I want to write a function that removes the first and last elements of a list.
I do not need to check if the length of the list. I found in a book I have a way to remove the first element and a way to reverse the list, but I think this may be more complex than it has to be. I also know that complexity aside, what I have is wrong after the second line, but I don't know why or how to fix it.
Here is my code:
midList :: [a] -> [a]
midList [a] = tail [a]
[a] = reverse [a]
[a] = tail [a]
[a] = reverse [a]
Upvotes: 1
Views: 2260
Reputation: 116139
You need to compose your steps functionally. Your proposed code could be rewritten as:
midList :: [a] -> [a]
midList xs = reverse (tail (reverse (tail xs)))
Note that tail
will crash your program if it is ever passed the empty list. You can, and should, avoid that by either 1) checking if the list has at least two elements, or 2) using drop 1
instead of tail
, which will simply return []
on the empty list.
If you try 2), you can keep your function type. For approach 1), instead, you can report the error to the caller using a Maybe [a]
return type. More concretely:
midList :: [a] -> Maybe [a]
midList xs@(_:_:_) = Just (reverse (tail (reverse (tail xs))))
midList _ = Nothing
where the pattern _:_:_
stands for a list which has at least two elements.
Alternatively, use both init
and tail
: the former removes the last element, the latter removes the first element.
midList :: [a] -> Maybe [a]
midList xs@(_:_:_) = Just (init (tail xs))
midList _ = Nothing
Be however quite careful here, since in general it is best to avoid partial functions like init
and tail
. As a general rule, one should never use them if a more basic approach does the job, e.g. using pattern matching.
Here, I would find the code above to be suboptimal. I'd prefer the following, coding my own "safe" version of init
to remove the last element, if any, or report the error otherwise.
midList :: [a] -> Maybe [a]
midList (_:xs@(_:_)) = safeInit xs
midList _ = Nothing
safeInit :: [a] -> Maybe [a]
safeInit [] = Nothing
safeInit (x:xs) = case safeInit xs of
Nothing -> Just []
Just ys -> Just (x:ys)
Upvotes: 1
Reputation: 48581
The first thing to do is to make sure you understand the problem. In particular, what do you want to happen if the list has no elements, or just one? The easiest thing is probably to just return the empty list in that case. So let's do that. We can break up the problem into two pieces:
The Haskell prelude defines a couple functions we really don't want, so let's hide them:
import Prelude hiding (tail, init)
Now we can define better versions of those functions. tail
will return all but the first element of a list and init
will return all but the last element. Then we can write
chop :: [a] -> [a]
chop xs = init (tail xs)
tail
is the easiest to write. I'll provide the skeleton and you can fill in the missing bits.
tail :: [a] -> [a]
tail [] = ????
tail (x : xs) = ????
init
is only a little bit trickier. Note that one of the cases will be recursive.
init :: [a] -> [a]
init [] = ????
init [x] = ????
init (x : xs) = ????
Upvotes: 4