如何递归定义我自己的haskell函数来附加两个列表?

时间:2016-02-16 20:55:14

标签: haskell functional-programming

如何定义一个带有两个列表和输出1列表的追加函数。

因此plusplus [1,2,3] [4,5,6]应该返回[1,2,3,4,5,6]

到目前为止,我有以下内容:

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

我想定义自己的++函数,但是使用递归。当我用两个列表实际执行它时,上面的代码给出了一个错误。

这是我通过命令获得的错误示例:

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

4 个答案:

答案 0 :(得分:5)

好吧,基本上你遇到的唯一问题是缺少案例:

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

缺失(并会导致错误)

但你可以清理一下:

前两种情况基本相同:无论第二个输入是什么结果 - 所以你可以把它作为一个名字(比如说ys)并写一下:

plusplus [] ys = ys

(你能看到这包括你的前两个案例吗? - 那些ys是什么?)

你的第3行将被正确的最后一个案例所覆盖(见下文) - 要看到这一点,请记住[x] == x:[]

我会在几分钟内给你完整的答案 - 但你应该试着弄明白:

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

尝试使用

找到要替换???的正确内容

你几乎拥有它 - 答案可能是:

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

你可以看到你永远不必触摸第二个列表 - 但需要重新创建所有第一个列表(链接到第二个列表)

答案 1 :(得分:2)

我们知道plusplus = flip (foldr (:))。以下是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

在专业化方面,我们得到:

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

这里是专业功能的定义:

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

当然,我们需要flip论点:

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

这就是为什么plusplus被定义的原因。

答案 2 :(得分:1)

谢谢你们!在卡斯滕的帮助下发现了我的愚蠢错误。

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)

这很有效。 所以对于输入:

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

输出是:

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

答案 3 :(得分:1)

对卡斯滕的回答来说,这真是一个很好的回答,但我认为这值得进入并且不适合评论。避免函数定义中非穷举模式错误的关键之一是以更机械,公式化的方式处理写入函数。列表类型的定义如下:

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

因此列表类型有两个数据构造函数[]:。因此,在启动函数时首先要尝试的是编写两个方程式,每个方程式对应一个构造函数。 Carsten的模板(我现在主要是复制它)遵循这种模式:

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

既然我们为这个类型的每个构造函数编写了一个模式,我们现在保证我们的模式是详尽的。所以我们盯着我们得到的东西,现在我们看到了解决方案:

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

让我们尝试另一个!签名:

-- | 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 = _

让我们通过根据两个构造函数拆分xs变量将其扩展为两个方程式,并在右侧填写简单部分:

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

现在我们可以采取两种方式。第一个是我们可以采用第二个等式并将其分为两个ys的案例:

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

填写这些空白很容易:

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

第二种方法是,不要在构造函数的情况下拆分ys,而要注意这是有效的:

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

因此,基于构造函数系统地进行处理,我们确信所有组合都已涵盖。

相关问题