将三个列表组合成Haskell中的3元组列表

Vic*_*Vic 1 haskell tuples list

我正在尝试编写一个函数,它将三个列表作为参数,并连续创建一个列表,每个列表都有一个三元组.

我给出的例子就是:zip3Lists [1, 2, 3] [4, 5, 6] ['a', 'b', 'c']会产生[(1, 4, 'a'), (2, 5, 'b'), (3, 6, 'c')].

到目前为止我所拥有的是:

zipThree [] [] [] = []
zipThree [] [] [x] = [x]
zipThree [] [x] [] = [x]
zipThree [x] [] [] = [x]
zipThree (x:xs) (y:ys) (z:zs) = (x, y, z) : zipThree xs ys zs
Run Code Online (Sandbox Code Playgroud)

它给了我这个错误:

haskell1.hs:32:33: error:
    • Occurs check: cannot construct the infinite type: c ~ (c, c, c)
      Expected type: [c]
        Actual type: [(c, c, c)]
    • In the expression: (x, y, z) : zipThree xs ys zs
      In an equation for ‘zipThree’:
          zipThree (x : xs) (y : ys) (z : zs) = (x, y, z) : zipThree xs ys zs
    • Relevant bindings include
        zs :: [c] (bound at haskell1.hs:32:27)
        z :: c (bound at haskell1.hs:32:25)
        ys :: [c] (bound at haskell1.hs:32:20)
        y :: c (bound at haskell1.hs:32:18)
        xs :: [c] (bound at haskell1.hs:32:13)
        x :: c (bound at haskell1.hs:32:11)
        (Some bindings suppressed; use -fmax-relevant-binds=N or -fno-max-relevant-binds)
Run Code Online (Sandbox Code Playgroud)

Jam*_*ton 6

首先让我们添加一个类型签名.从问题来看,似乎以下类型签名是合适的: zipThree :: [a] -> [b] -> [c] -> [(a, b, c)]

这需要3个列表(包含可能不同类型的对象),然后生成三元组列表.

您处理空列表案例: zipThree [] [] [] = []

然后问题就出现了.正如评论中所述,您可以看到列表具有不同的长度但是提供不同类型的输出.

我将为每行旁边的类型添加注释,以便您可以看到:

zipThree [] [] [x] = [x] :: [c]
zipThree [] [x] [] = [x] :: [b]
zipThree [x] [] [] = [x] :: [a]
Run Code Online (Sandbox Code Playgroud)

这些不适合具有类型的其他两种情况[(a, b, c)].

您在评论中提到,您只需假设长度相同,因此只需删除这些情况就足够了.这给出了:

zipThree [] [] [] = []
zipThree (x:xs) (y:ys) (z:zs) = (x, y, z) : zipThree xs ys zs
Run Code Online (Sandbox Code Playgroud)

这为[(1, 4, 'a'), (2, 5, 'b'), (3, 6, 'c')]您给出的输入()提供了正确的输出([1, 2, 3] [4, 5, 6] ['a', 'b', 'c']).

当然,对于列表长度不同的输入,此功能将失败.停止直接错误并允许您处理问题的一种方法是将结果包装在Maybe中.

首先,我们需要将类型更改为: zipThree :: [a] -> [b] -> [c] -> Maybe [(a, b, c)]

Maybe数据类型可以是Just in Just a或包装的值Nothing.

对于空列表,我们只想给出空列表: zipThree [] [] [] = Just [].

当然,你可能认为下一个案例应该是: zipThree (x:xs) (y:ys) (z:zs) = Just $ (x, y, z) : zipThree xs ys zs.

但这不起作用.不要忘记zipThree xs ys zs现在有类型Maybe [(a, b, c)](x, y, z)有类型(a, b, c)所以我们不能将它添加到列表中.

我们需要做的是检查zipThree xs ys zs在递归过程中某个时刻是否失败的结果然后Nothing我们只想Nothing再次传递它.如果它成功并给了我们,Just as那么我们想要将我们添加(x, y, z)到该列表中.我们可以使用case of以下方法检查相关的案例:

zipThree (x:xs) (y:ys) (z:zs) = case zipThree xs ys zs of
    Nothing -> Nothing
    Just as -> Just $ (x, y, z) : as
Run Code Online (Sandbox Code Playgroud)

如果在递归期间某些列表为空而其他列表不为空,我们将知道列表的长度不同.这不符合我们目前所面对的任何图案[] [] [](x:xs) (y:ys) (z:zs)因此我们需要一个最终的包罗万象的情况下给我们,Nothing并防止错误:

zipThree _ _ _ = Nothing

这给出了最终定义:

zipThree :: [a] -> [b] -> [c] -> Maybe [(a, b, c)]
zipThree [] [] [] = Just []
zipThree (x:xs) (y:ys) (z:zs) = case zipThree xs ys zs of
    Nothing -> Nothing
    Just as -> Just $ (x, y, z) : as
zipThree _ _ _ = Nothing
Run Code Online (Sandbox Code Playgroud)

这些例子的结果是:

zipThree [1, 2, 3] [4, 5, 6] ['a', 'b', 'c', 'd'] = Nothing

zipThree [1, 2, 3] [4, 5, 6] ['a', 'b', 'c'] = Just [(1, 4, 'a'), (2, 5, 'b'), (3, 6, 'c')].

希望这有帮助,随时要求澄清:)

编辑:正如评论中所建议的那样,在列表长度不同的情况下,以下定义会停止:

zipThree :: [a] -> [b] -> [c] -> [(a, b, c)]
zipThree (x:xs) (y:ys) (z:zs) = (x, y, z) : zipThree xs ys zs
zipThree _ _ _ = []

zipThree :: [a] -> [b] -> [c] -> Maybe [(a, b, c)]
zipThree (x:xs) (y:ys) (z:zs) = case zipThree xs ys zs of
    Nothing -> Just [(x, y, z)] -- Change is here
    Just as -> Just $ (x, y, z) : as
zipThree _ _ _ = Nothing
Run Code Online (Sandbox Code Playgroud)

PS感谢那个在编辑中添加了失踪的人.