简体   繁体   中英

How to recursively define my own haskell function for appending two lists?

How would I define an append function that takes two lists and output 1 list.

So plusplus [1,2,3] [4,5,6] should return [1,2,3,4,5,6] .

I have the following so far:

plusplus :: [Int] -> [Int] -> [Int]
plusplus [] [] = []
plusplus [] [x] = [x]
plusplus [x] [] = [x]
plusplus (x:xs) (y:ys) = x: plusplus xs (y:ys)

I want to define my own ++ function, but with recursion. The code above gives an error when I actually execute it with two lists.

This is an example of an error I get with the command:

plusplus [1,2,3] [4,5,6] 
[1,2,3*** Exception: baby.hs:(...): Non-exhaustive patterns in function plusplus

ok basically the only problem you have here is a missing case:

pluspluse (x:xs) [] = ...

is missing (and will cause an error)

but you can clean up quite a bit:

The first two cases are basically the same: whatever the second input is will be the result - so you can just it those a name (say ys ) and write:

plusplus [] ys = ys

(can you see how this includes your first two cases? - what is ys in those?)


your 3rd line one will be covered by the correct last case (see below) - to see this remember that [x] == x:[]


I'll give you the complete answer in a few minutes - but you should try to figure it out:

plusplus :: [Int] -> [Int] -> [Int]
plusplus [] ys = ys
plusplus (x:xs) ? = ??

try to find the correct things to replace ? and ?? with


you almost had it - and the answer can be:

plusplus :: [Int] -> [Int] -> [Int]
plusplus [] ys = ys
plusplus (x:xs) ys = x: plusplus xs ys

as you can see you never have to touch the second list - but need to recreate all of the first list (to link to the second)

We know that plusplus = flip (foldr (:)) . Here's the definition of foldr :

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr step = let fold acc []     = acc
                 fold acc (x:xs) = step x (fold acc xs)
             in  fold

On specializing, we get:

foldr     :: (a ->  b  ->  b)  ->  b  -> [a] ->  b
              |     |      |       |      |      |
              |     |      |       |      |      |
      (:) :: (a -> [a] -> [a])     |      |      |
                                   |      |      |
foldr (:) ::                      [a] -> [a] -> [a]

Here's the definition of the specialized function:

fold :: [a] -> [a] -> [a]
fold acc []     = acc
fold acc (x:xs) = x : fold acc xs

Ofcourse, we need to flip the arguments:

plusplus :: [a] -> [a] -> [a]
plusplus []     ys = ys
plusplus (x:xs) ys = x : plusplus xs ys

That's the reason why plusplus is defined the way it is.

Thanks guys! With the help of Carsten spotting my silly mistake.

plusplus :: [Int] -> [Int] -> [Int]
plusplus [] [] = []
plusplus (x:xs) [] = x:xs
plusplus [] (x:xs) = x:xs
plusplus (x:xs) (y:ys) = x: plusplus xs (y:ys)

This works. So for input:

plusplus [1,2,3] [4,5,6]

Output is:

[1,2,3,4,5,6]

This is really a riff on Carsten's answer, but I think it's worth going into and won't fit into a comment. One of the keys to avoiding non-exhaustive pattern errors in function definitions is to approach writing functions in a more mechanical, formulaic manner. The definition of the list type is something like this:

data [a] = [] | a : [a]

So list types have two data constructors, [] and : . So a good first thing to try when starting your function is to write two equations, one for each of the constructors. Carsten's template (which I'm mostly copying now) is following that pattern:

plusplus :: [Int] -> [Int] -> [Int]
plusplus []     ys = _                -- Equation for empty list
plusplus (x:xs) ys = _                -- Equation for list cells

Now since we've written one pattern for each of the constructors of the type, we're now guaranteed that our patterns are exhaustive. So we stare at what we got a bit, and now we see the solution:

plusplus :: [Int] -> [Int] -> [Int]
plusplus []     ys = ys
plusplus (x:xs) ys = x : plusplus xs ys

Let's try another one! Signature:

-- | Merge two lists by alternating elements of one with the
-- other.  After either list runs out, just continue with the
-- remaining elements of the other.
interleave :: [a] -> [a] -> [a]
interleave xs ys = _

Let's expand this to two equations by splitting the xs variable according to the two constructors, and fill in the easy parts on the right hand side:

interleave :: [a] -> [a] -> [a]
interleave [] ys     = ys       -- this one was easy
interleave (x:xs) ys = x : _    -- we know it has to start with `x`

Now we can go two ways. The first one is we could take the second equation and split it into two cases for ys :

interleave :: [a] -> [a] -> [a]
interleave []     ys     = ys
interleave (x:xs) []     = x : _
interleave (x:xs) (y:ys) = x : _

And filling in those blanks is easy:

interleave :: [a] -> [a] -> [a]
interleave []     ys     = ys
interleave (x:xs) []     = x : xs
interleave (x:xs) (y:ys) = x : y : interleave xs ys

The second way is, instead of splitting ys on the constructors' cases, to notice that this works:

interleave :: [a] -> [a] -> [a]
interleave []     ys = ys
interleave (x:xs) ys = x : interleave ys xs

So by going about it systematically based on the constructors we know for sure that all combinations are covered.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM