Haskell如何将group167返回的列表的列表转为扁平列表?
嘿,这个需求其实特别好理解,咱们先理清楚现有代码的逻辑,再一步步实现扁平化~
首先,你的group167函数是这样的:
import qualified Data.List as DL group167 :: (Eq a, Ord a) => [a] -> [[a]] group167 = DL.group . DL.sort
它的作用是先对输入列表排序,再把相邻的相同元素分组,最终输出一个嵌套列表(比如输入[3,1,2,1,3],会先变成[1,1,2,3,3],再分组为[[1,1],[2],[3,3]])。现在要把这个嵌套列表转回单层列表,Haskell里有好几种方式,咱们一个个说:
1. 最简洁的方式:用concat函数
Haskell标准库的Data.List里提供了concat函数,它的类型签名是:
concat :: [[a]] -> [a]
正好能把嵌套的列表打平——它会把所有子列表按顺序拼接成一个单层列表。你可以直接把它和group167组合起来:
flattenedGroup167 :: (Eq a, Ord a) => [a] -> [a] flattenedGroup167 = concat . group167
或者直接把整个调用链写全:
flattenedGroup167 = concat . DL.group . DL.sort
比如刚才的例子,flattenedGroup167 [3,1,2,1,3]就会返回[1,1,2,3,3],完美符合需求。
2. 理解底层逻辑:用foldr手动拼接
其实concat本质上就是foldr (++) []的别名,咱们可以用foldr来手动实现同样的效果,这样能更清楚看到扁平化的逻辑:
flattenedGroup167 = foldr (++) [] . group167
这里的逻辑是:用foldr遍历嵌套列表的每个子列表,把每个子列表通过(++)(列表拼接操作符)和后续的结果结合,初始值是空列表[]。最终就把所有子列表依次拼接成了单层列表。
3. 自己写递归函数:彻底搞懂原理
如果想完全理解扁平化的底层逻辑,咱们可以自己写一个递归的扁平化函数:
flatten :: [[a]] -> [a] flatten [] = [] -- 空的嵌套列表,返回空 flatten (x:xs) = x ++ flatten xs -- 把第一个子列表x,和剩下的嵌套列表xs的扁平化结果拼接
然后把它和group167组合:
flattenedGroup167 = flatten . group167
这个递归函数的逻辑非常直观:遇到空列表就返回空;遇到非空的嵌套列表,就先取第一个子列表,再把它和剩下部分的扁平化结果拼起来,直到所有子列表都被处理完。
小补充:其实结果等价于直接排序?
哦对了,还有个小细节:因为group167是先排序再分组,分组后再扁平化,其实最终结果和直接调用DL.sort是一样的😂。不过这只是这个特定场景的巧合,重点还是学习扁平化嵌套列表的方法~
内容的提问来源于stack exchange,提问作者Madderote

