如何通过交错haskell中的元素将列表列表组成一个列表?

时间:2014-02-17 06:14:08

标签: haskell

如何获取列表的输入列表和

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

haskell中的这种格式的输出列表?

[ 1,
  2,5,
  3,6,9,
  4,7,10,13]

使用此格式

rearrange     :: [[a]] -> [a]
rearrange xs  = ??

4 个答案:

答案 0 :(得分:2)

看起来你想要通过对角线重构元素。

这是一些指导......

将结果列表写为串联:

[a0] ++ [a1,b0] ++ [a2,b1,c0] ++ ...

并根据辅助函数表达每个组件:

[a0] ++ ...       = go 1 [ as, bs, cs, ds, ...]
[a1,b0] ++ ...    = go 2 [ tail as, bs, cs, ds, ...
[a2,b1,c0] ++ ... = go 3 [ tail (tail as), tail bs, cs, ds, ...]

,其中

as = [a0, a1, a2, ...]
bs = [b0, b1, b2, ...]
etc.

递归看起来像:

go n [lists of lists] = some-list ++ go (n+1) [new lists of lists]

更具体地说,go n [list1, list2, ...]将前n个列表的头部放入列表并与

连接。
go (n+1) [list1', list2', ...]

,其中

listk' = tail listk      if k <= n
       = listk           otherwise

go nhead应用于第一个n列表以创建下一个对角线,并将tail应用于前n个列表以传递给go (n+1)

http://lpaste.net/100055

答案 1 :(得分:1)

我误读了OP想要耗尽2D列表中的所有元素。这是我最初使用的具有正确行为的函数的一个版本:

pyramidT :: [[a]] -> [[a]]
pyramidT [] = [[]]
pyramidT (row:rows) = zipWith (:) row ([]:(pyramidT rows)) 

rearrangeT :: [[a]] -> [a]
rearrangeT = concat . pyramidT

-- Ghci> pyramidT testData
-- [[1],[2,5],[3,6,9],[4,7,10,13]]

-- Ghci> rearrangeT testData
-- [1,2,5,3,6,9,4,7,10,13]

(上一篇文章:)

这是一个递归解决方案。我不喜欢我没有适当的高阶函数来替换助手h。值h a b旨在模拟zipWith (:) a b,但只要ab的长度不同,就会执行适当的操作。

import Data.List

-- | Compute the list of diagonals
pyramid :: [[a]] -> [[a]]
pyramid [] = [[]]
pyramid (row:rows) = h row ([]:(pyramid rows)) where
  h [] rs = rs                      -- pad left input with "empty" elements
  h ls [] = map (:[]) ls            -- pad right input with []s
  h (l:ls) (r:rs) = (l:r):(h ls rs) -- act as zipWith would

-- | Compute the interleaving 
rearrange :: [[a]] -> [a]
rearrange = concat . pyramid

testData :: [[Int]
testData = [[1,2,3,4]
           ,[5,6,7,8]
           ,[9,10,11,12]
           ,[13,14,15,16]]

-- Ghci> pyramid testData
-- [[1],[2,5],[3,6,9],[4,7,10,13],[8,11,14],[12,15],[16]]

-- Ghci> rearrange testData
-- [1,2,5,3,6,9,4,7,10,13,8,11,14,12,15,16]

答案 2 :(得分:1)

观察该任务确实告诉您考虑分别构建每一行:

[ 1,
  2,5,
  3,6,9,
  4,7,10,13]

这是一个很好的提示,您应该考虑构建列表列表,然后将它们连接起来。

为了构建列表列表,沿着第一个列表并将其作为头部附加到其他列表将是有用的,直到用完第一个列表中的元素。所以,它是一个zipWith (:),以某种方式折叠在列表列表上。

最后一行如何折叠?你在哪里获得列表为最后一行添加头?你如何获得正确数量的名单?好吧,如果我们可以获得无限量的空列表,那么最后一行将把它压缩成单例(只有一个值的列表)。

Prelude> zipWith (:) [13..16] (repeat [])
[[13],[14],[15],[16]]

现在,归纳步骤:倒数第二行([9..12])必须在尾部列表中添加一个新头([[13],[14],[15],[16]]),但是这样,第一个元素产生一个单例[9],第二个元素10附加到尾部列表的第一个单例[13]。所以我们必须在尾部添加一个空列表:

Prelude> zipWith (:) [9..12] $ [] : zipWith (:) [13..16] (repeat [])
[[9],[10,13],[11,14],[12,15]]

现在很容易看到如何折叠

rearrange = concat . foldr (\hs ts -> zipWith (:) hs ([]:ts)) (repeat [])

我们来看看:

Prelude> :t rearrange
rearrange :: [[a]] -> [a]
Prelude> rearrange [[1..4], [5..8], [9..12], [13..16]]
[1,2,5,3,6,9,4,7,10,13]

答案 3 :(得分:0)

这是一个嵌套的for循环。我们可以用这样的命令式语言(在一个邪恶的混合伪代码中)来实现这一点

int **xs; -- assuming an infinite list of infinite lists
int* output = [];
for (i = 0; ; i++)
  for (j = 0; j <= i; j++)
    output.append(xs[IDX1][IDX2])

这可以很容易地表达为您尝试的列表理解:

[(xs !! IDX1) !! IDX2 | i <- [0..], j <- [0..i]]

我会把索引留给你;锻炼并不困难。

请注意,这是针对无限列表的。如果此输入列表是有限的,则必须添加某种边界检查以确保不超过任何索引。

以下是无限列表的示例:

ys = map (\i -> [10*i,10*i+1..]) [0..]

这导致列表:

[[0,1,2..],
 [10,11,12..],
 [20,21,22..],
 [30,31,32..],
 ...]

如果我们定义

rearrange xs = [(xs !! IDX1) !! IDX2 | i <- [0..], j <- [0..i]]

然后

take 10 $ rearrange ys 

输出

[0,1,10,2,11,20,3,12,21,30]