您的 interChange 与 Prelude 的 zip 相同。
ghci> zip [1,2,3] ["wibble", "wobble", "wubble"]
[(1,"wibble"),(2,"wobble"),(3,"wubble")]
要编写您指定的函数,您实际上需要一对相同类型的列表。我们正在输出一个特定类型的列表,因此进入该列表的所有元素也必须具有该类型。
interleave :: [a] -> [a] -> [a]
您可以通过传递Bool 以递归方式实现此功能,指示您应该从哪个列表中获取下一个元素:
interleave = go True
where go _ [] ys = ys
go _ xs [] = xs
go True (x:xs) ys = x : go False xs ys
go False xs (y:ys) = y : go True xs ys
评估go 的最后两个子句之间的乒乓球,直到其中一个输入列表为空,然后我们只返回另一个输入列表的其余部分。 (如果您希望它的行为更像 zip,则可以通过在这些情况下返回 [] 而不是 xs 或 ys 来截断输出。)
但我始终建议尽可能避免递归到更高级别的编程。我们可以把这个函数写成管道:
- 使用
zip 配对输入列表的元素
- 将结果列表中的每个元组变成一个二元素列表:
map (\(x, y) -> [x, y])
- 使用
concat 展平生成的列表列表
所以代码看起来像这样:
interleave xs ys = concat $ map (\(x, y) -> [x, y]) $ zip xs ys
我发现此代码比递归代码更容易理解,递归代码需要您对控制流进行推理 - 它只是一系列高级指令。
顺便说一句,您可以通过将中间map 拖到concat 或zip 中来省略它:
interleave xs ys = concatMap (\(x, y) -> [x, y]) $ zip xs ys
interleave xs ys = concat $ zipWith (\(x, y) -> [x, y]) xs ys