splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith f [] = []
splitWith f list = pre : (splitWith f suf)
where (pre, suf) = break f list
此函数应根据谓词拆分列表。但我得到了无限的递归。
答案 0 :(得分:4)
break
定义为:
<强>
应用于谓词break :: (a -> Bool) -> [a] -> ([a], [a])
强>break
和列表p
的
xs
返回一个元组 其中第一个元素是{strong>xs
的最长前缀(可能为空) 不满足p
和第二个元素的元素是余数 列表:break (> 3) [1,2,3,4,1,2,3,4] == ([1,2,3],[4,1,2,3,4]) break (< 9) [1,2,3] == ([],[1,2,3]) break (> 9) [1,2,3] == ([1,2,3],[])
因此,一旦完成了第一个break
,所有剩余的break
将简单地将列表拆分为空列表和原始列表。结果,可以说模式没有进展。除非所有元素都不满足谓词,否则你将继续迭代第一个元素满足谓词的列表,并且永远不会删除它。
您可能希望将break
与span
:
splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith f [] = []
splitWith f list = pre1 : pre2 : (splitWith f suf2)
where (pre1, suf1) = break f list
(pre2, suf2) = span f suf1
这将在谓词不满足的元素列表中交叉划分给定列表,并将列表满足。
如果你不想要后者,你可以简单地dropWhile
:
splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith f [] = []
splitWith f list = pre : (splitWith f $ dropWhile f suf)
where (pre1, suf) = break f list
答案 1 :(得分:2)
这是因为这将不断添加一个空列表到最后。
如果您从无限集合中获取一些任意数量的值,则可以看到这是:
*Main> take 10 $ splitWith (==5) [1,2,3,4,5]
[[1,2,3,4],[],[],[],[],[],[],[],[],[]]
如果您break (==5) [5]
,则结果为([],[5])
,其格式匹配为pre
[]
和suf
[5]
。下一次迭代得到相同的break (==5) [5]
来评估......所以它就这样了。
<强>更新强>
我不确定你所遵循的确切语义,但这可能有助于制定你想要的功能:
splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith f [] = []
splitWith f xs = doSplitWith [] xs
where
doSplitWith first second @ (y:ys) =
if f y
then (reverse first) : [second]
else doSplitWith (y:first) ys
splitWith' f xs = takeWhile (not . f) xs : [dropWhile (not . f) xs]
我猜这更像是splitAt
或其他东西,不是吗?