如何获取列表的输入列表和
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 = ??
答案 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 n
将head
应用于第一个n列表以创建下一个对角线,并将tail
应用于前n个列表以传递给go (n+1)
答案 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
,但只要a
和b
的长度不同,就会执行适当的操作。
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]