Reputation: 447
I'm new Haskell and try to practice my Haskell skill.
I want to implement a function to merge two lists so that the items are alternative from each list.
["a", "b", "c"]
["1", "2", "3"]
After the merger.
["a", "1", "b", "2", "c", "3"]
Here is my Haskell code
mergeList::[String]->[String]->[String]
mergeList [] [] = []
mergeList _ [] = []
mergeList [] _ = []
mergeList (x:xs) (y:ys) = x:y:mergeList xs ys
The code works as long as the lengths of two lists are the same.
But I want to have some error checking on the lengths of the two lists.
If the length of two lists are different, then I want to report an error.
mergeList::[String]->[String]->[String]
mergeList l r | length l /= length r = error "report an error"
| otherwise =
How can I finish the otherwise section?
Upvotes: 1
Views: 2357
Reputation: 9103
Your otherwise function it might be something like this:
otherwise = reverse $ foldl (\a (x,y) -> x:y:a) [] (x `zip` y)
Example:
Prelude> let y = ["a", "b", "c"]
Prelude> let x = ["1", "2", "3"]
Prelude> reverse $ foldl (\a (x,y) -> x:y:a) [] (x `zip` y)
["a","1","b","2","c","3"]
or the elliptic00 solution with foldr that avoids the usage of the reverse
:
otherwise = foldr ((x, y) a -> x:y:a) [] (x `zip` y)
Upvotes: 1
Reputation: 48581
luqui points out that we like to be safe in Haskell, avoiding error
. But waiting till we get to the end of at least one list before yielding anything throws away any hope of laziness. One option is to toss error handling to the caller. We can fake up lists like this:
import Data.Bifunctor
import Data.Bifunctor.Fix
import Data.Bifunctor.Tannen
data ListF l a = NilF | ConsF a l
instance Bifunctor ListF where
bimap _ _ NilF = NilF
bimap f g (ConsF a l) = ConsF (f a) (g l)
{-instance Bifoldable ListF where ...
instance Bitraversable ListF where ... -}
type List = Fix ListF
But we can add in error handling:
type EList e = Fix (Tannen (Either e) ListF)
An EList e a
is either an error of type e
, a NilF
or a ConsF
holding an a
and another EList e a
.
This composition approach, however, ends up being very verbose, confusing, and somewhat inefficient. A better approach in practice is to use a totally custom list type:
data EList' e a = Error e | Nil | Cons a (EList' e a)
Upvotes: 0
Reputation: 165
In your pattern matching raise an error if EXACTLY one of the lists is empty. So you would have
mergeList (x: xs) (y: ys) = x: y: mergeList xs ys
mergeList [] [] = []
mergeList _ _ = error "your error" -- falling through to this pattern means that exactly one of the lists is empty
It is somewhat redundant to check whether lengths of 2 lists are equal, since 'length' has O(n) complexity, meaning that you would go over lists twice (in a case of equal lengths).
As luqui correctly pointed out, there are more elegant ways to solve the problem using Maybe type. When you learn about functors (and Maybe is an instance of class Functor), you could write:
mergeList :: [a] -> [a] -> Maybe [a]
mergeList (x: xs) (y: ys) = fmap (\rest -> x: y: rest) $ mergeList xs ys
mergeList [] [] = Just []
mergeList _ _ = Nothing
Upvotes: 2
Reputation: 45736
The easiest way would probably just to do a check beforehand, then route the main work to an auxiliary function:
mergeList::[String]->[String]->[String]
mergeList xs ys
| length xs /= length ys = error "Lists must be the same length"
| otherwise go xs ys
where
go [] [] = []
go _ [] = []
go [] _ = []
go (x:xs) (y:ys) = x:y:go xs ys
The check is done in the main function, and if the list's are the same size, they're handed to an internal function that does the main processing. Note how go
is just your original function; it's just been internalized within a wrapper function. You could use a more descriptive name than go
; I was trying to keep it brief.
It's been awhile since I've written Haskell, so I apologize for any syntax issues.
You could also generalize the function by changing the signature to:
mergeList::[a]->[a]->[a]
Since you're not doing anything specific to Strings in the function.
Upvotes: 2
Reputation: 60463
Using error
is discouraged. In Haskell we like to define our functions for all possible inputs, so that the type system can guide us to write correct programs. If your function is invalid for lists of different lengths, one way would be to use Maybe
.
mergeList :: [a] -> [a] -> Maybe [a]
mergeList [] [] = Just []
mergeList (x:xs) (y:ys) =
case mergeList xs ys of
Just merged -> Just (x:y:merged)
Nothing -> Nothing
mergeList _ _ = Nothing
There are more succinct ways to write this, but I'm staying near the ground floor.
Now users of mergeList
are required to define what to do in the case that they passed lists of two different lengths.
Upvotes: 8