List 在Haskell中合并两个列表

List 在Haskell中合并两个列表,list,haskell,merge,functional-programming,interleave,List,Haskell,Merge,Functional Programming,Interleave,无法想出如何在Haskell中以以下方式合并两个列表: INPUT: [1,2,3,4,5] [11,12,13,14] OUTPUT: [1,11,2,12,3,13,4,14,5] 编辑:看看Ed'ka的回答和评论 另一种可能性: merge xs ys = concatMap (\(x,y) -> [x,y]) (zip xs ys) 或者,如果您喜欢Applicative: merge xs ys = concat $ getZipList $ (\x y -> [x

无法想出如何在Haskell中以以下方式合并两个列表:

INPUT:  [1,2,3,4,5] [11,12,13,14]

OUTPUT: [1,11,2,12,3,13,4,14,5]

编辑:看看Ed'ka的回答和评论

另一种可能性:

merge xs ys = concatMap (\(x,y) -> [x,y]) (zip xs ys)
或者,如果您喜欢Applicative:

merge xs ys = concat $ getZipList $ (\x y -> [x,y]) <$> ZipList xs <*> ZipList ys
merge xs ys=concat$getZipList$(\x y->[x,y])ZipList xs ZipList ys

那么为什么你认为简单(concat.transpose)“不够漂亮”?我想你试过这样的方法:

merge :: [[a]] -> [a]
merge = concat . transpose

merge2 :: [a] -> [a] -> [a]
merge2 l r = merge [l,r]

因此,您可以避免显式递归(相对于第一个答案),但它仍然比第二个答案简单。那么缺点是什么呢

我想提出一个更懒惰的合并版本:

merge [] ys = ys
merge (x:xs) ys = x:merge ys xs
例如,您可以查看最近的SO问题。

在第二个论点中,被接受答案中的版本不必要地严格,这就是这里改进的地方。

当然是一个展开的例子:

-- ++
pp [] [] = []
pp [] (h:t) = h:pp [] t
pp (h:t) [] = h:pp t []
pp (h:t) (a:b) = h : pp t (a:b)
interleave :: [a] -> [a] -> [a]
interleave = curry $ unfoldr g
  where
    g ([], [])   = Nothing
    g ([], (y:ys)) = Just (y, (ys, []))
    g (x:xs, ys) = Just (x, (ys, xs))


通常,如果你解释你尝试了什么以及为什么不起作用,你会学到更多,这样人们就可以填补空白,而不仅仅是给你一大块代码。相关:啊,我忘了转置,错过了评论。非常好,+1(但我不一定说这比我的第一个解决方案简单得多)同意。您的解决方案可能更简单。。但真正的问题是,它不是100%正确的:对于不同长度的列表(如问题中的示例输入),它没有按预期工作(缺少尾随“5”)。很好!我忽略了示例输出中的5。我将用指向您的答案和评论的指针更新我的答案。谢谢看起来两者都是O(n),尽管显式递归速度快2倍多,而且对数据的空间效率高。使用“ghc-O2”实现列表(这是预期的,后者会生成大量中间列表)。然而,我怀疑,如果使用“转置”和“concat”的“流融合”实现,那么区别就不那么明显了。主要缺点是,普通人看它时必须盯着它思考一段时间,才能理解它为什么会起作用,而其他解决方案则是显而易见的。您的解决方案非常优雅。好吧,这将ys的所有元素都放在了最后,所以它不起作用。但我认为你的意思是颠倒安德里解中前两个方程的顺序。不,它做的是相同的事情——在每个列表之间交替。请注意,
xs
ys
在递归调用中交换。这是一个很好的解决方案!我希望我自己能想到类似的东西为什么这个版本是懒惰的,而类似于`合并(x:xs)(y:ys)=x:y:merge xs ys-merge xs[]=xs-merge[]ys=ys`的东西不是?@Shitikanth你看过我答案中的链接了吗?这是一个例子,您需要这个版本的merge的额外惰性。您的合并也是惰性的,但它不必要地通过模式匹配强制第二个参数。此解决方案不正确。最后一行应该是
pp(h:t)(a:b)=h:a:pp t b
。我对函数式编程是新手,代码让我想知道:尾部调用优化是否也适用于这种递归形式?不,它不适用。尾部调用是(:),不需要优化。中有一个更懒惰的版本。它在第二个参数中是惰性的。@Ingo关于这个奇怪的问题,在到达基本情况之前,它不是保留堆栈上的所有元素吗?这难道不能使堆栈溢出吗?@JustinMeiners如果(:)构造函数在第二个参数中是严格的,就会溢出。但是由于懒惰,它不会计算
merge xs ys
部分,直到调用方需要它。但是,创建该列表的调用已经返回<代码>交错[][1,2,3]将给出
[]
。我想现在应该行了。你的无同态又是一个例子!(那么它将等同于上面的内容)。@dfeuer(上面的评论)
interleave :: [a] -> [a] -> [a]
interleave = curry $ unfoldr g
  where
    g ([], [])   = Nothing
    g ([], (y:ys)) = Just (y, (ys, []))
    g (x:xs, ys) = Just (x, (ys, xs))