在Haskell中合并两个列表

bog*_*jov 24 merge haskell functional-programming list interleave

无法弄清楚如何在Haskell 中以下列方式合并两个列表:

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

OUTPUT: [1,11,2,12,3,13,4,14,5]
Run Code Online (Sandbox Code Playgroud)

Dan*_*kov 55

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

merge [] ys = ys
merge (x:xs) ys = x:merge ys xs
Run Code Online (Sandbox Code Playgroud)

对于一个示例用例,您可以查看最近关于延迟生成组合的 SO问题.
接受的答案中的版本在第二个参数中是不必要的严格,这是在这里改进的.

  • 不,它做同样的事情 - 在每个列表之间交替.请注意,在递归调用中交换了`xs`和`ys`. (14认同)
  • 这是一个很好的解决方案!我希望我自己也能想到这样的事情 (2认同)
  • @Shitikanth你看到我的回答中的链接了吗?这是一个例子,你需要这个合并版本的额外懒惰.你的合并也是懒惰的,但它通过模式匹配不必要地强制第二个参数. (2认同)

and*_*dri 43

merge :: [a] -> [a] -> [a]
merge xs     []     = xs
merge []     ys     = ys
merge (x:xs) (y:ys) = x : y : merge xs ys
Run Code Online (Sandbox Code Playgroud)

  • 我是函数式编程的新手,代码让我想知道:尾调用优化是否也适用于这种递归形式? (3认同)
  • 不,它没有。尾调用是 (:),不需要优化。 (2认同)

Ed'*_*'ka 23

那么为什么你认为那么简单(concat.transpose)"还不够"?我假设你尝试过类似的东西:

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

merge2 :: [a] -> [a] -> [a]
merge2 l r = merge [l,r]
Run Code Online (Sandbox Code Playgroud)

因此,您可以避免显式递归(与第一个答案相比),但仍然比第二个答案更简单.那有什么缺点呢?

  • 同意.你的解决方案可能更直接..它的真正问题在于它不是100%正确:对于不同长度的列表(如在问题的示例输入中)它不能按预期工作(拖尾) '5'缺失). (3认同)
  • 看起来两者都是O(n),尽管显式递归对于Data.List实现(预期 - 后者生成大量中间列表)的速度比"ghc -O2"快2倍以上且空间有效.但是我怀疑,如果使用"transpose"和"concat"的"stream-fusion"实现,差异就不那么明显了. (2认同)
  • 主要的缺点是,看着它的普通人必须盯着它并思考一段时间才能理解它的工作原理,而其他解决方案立即显而易见.你的解决方案非常优雅. (2认同)

dan*_*lei 6

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

另一种可能性

merge xs ys = concatMap (\(x,y) -> [x,y]) (zip xs ys)
Run Code Online (Sandbox Code Playgroud)

或者,如果你喜欢Applicative:

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

  • 是的,坏,只适用于等长列表. (3认同)