单独列出

时间:2015-03-22 21:30:19

标签: haskell

我在尝试分离列表时遇到问题,假设我们有以下列表


[[1,2,3,4], [5,6,7,8], [9,10,11,12 ], [13,14,15,16,17]].

结果应为:

[[1,5,9,13] [2,6,10,14] [3,7,11,16] [4,8,12,16]]

我试图按照以下方式进行:

joinHead (x: xs) = map head (x: xs)
separateLists (x: xs) = xs joinHead x ++ separateLists

显然这不起作用。我希望你能帮助我。 THX。

1 个答案:

答案 0 :(得分:0)

我调整了您编写的函数joinHeadseparateLists,以使代码工作,同时保留您所遵循的逻辑。从我可以推断出这些函数的推断,我们的想法是使用joinHead来提取每个子列表的第一个元素并返回一个新列表。然后,这个新列表应该插入到递归调用separateLists返回的列表列表的前面。

以下是joinHead的新定义:

joinHead :: [[a]] -> [a]
joinHead ([]:_) = []
joinHead xs     = map head xs

请注意,第一行通过模式匹配检查列表列表中包含的第一个列表是否为空,如果是,则返回空列表([])。原因是两个:

  1. 函数head不安全。这意味着在空列表上调用head将导致抛出异常(尝试在GHCi head []中运行);
  2. 为简单起见,我假设已检查所有列表的长度相同(length (xs !! 0) == length (xs !! 1) ...)。
  3. separateLists的定义如下:

    separateLists :: [[a]] -> [[a]]
    separateLists ([]:_)   = []
    separateLists ([x]:xs) = [joinHead ([x]:xs)]
    separateLists xs       = joinHead xs : separateLists (map tail xs)
    

    同样,前两个定义对于停止递归和安全目的都是必要的。第一行说:“如果第一个列表为空,则所有列表的所有元素都已消耗,因此返回[]”。第二行说:“如果第一行只有一个元素,那么只需调用joinHead并返回包含在列表中的结果”。请注意,在第三个定义中,我们调用了tail,与head一样,在[]上调用时会抛出异常。这就是为什么我们需要一个长度为1的列表的单独案例的原因。最后,为长度大于1的列表执行的第三行从joinHead xs获取一个列表并插入它(使用“缺点”在递归调用(:)返回的列表开头的“operator separateLists”。在此次调用中,我们必须删除所有列表中的第一个元素,这就是我们使用map tail xs的原因。

    现在,运行:

    λ: let list = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16,17]]
    λ: separateLists list
    [[1,5,9,13],[2,6,10,14],[3,7,11,15],[4,8,12,16]]
    

    会给你预期的结果。我希望它足够清楚。最后,我想指出这个实现远非最佳,并且正如评论中所建议的那样,您应该使用标准Data.List.transpose。作为一个运动和didatic的例子,它很好! ; - )