splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith f [] = []
splitWith f list = pre : (splitWith f suf)
where (pre, suf) = break f list
该函数应根据谓词拆分列表。但是我得到了无限的递归。为什么这个代码中的模式没有详尽?
splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith f [] = []
splitWith f list = pre : (splitWith f suf)
where (pre, suf) = break f list
该函数应根据谓词拆分列表。但是我得到了无限的递归。为什么这个代码中的模式没有详尽?
break
定义为:
break :: (a -> Bool) -> [a] -> ([a], [a])
break
,施加到谓词p
和列表xs
,返回一个元组 其中第一元件是最长前缀(可能为空)xs
of 不满足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
旨意,只需拆分列表为空列表和原始列表。因此,这种模式可以说没有任何进展。除非所有元素都不满足谓词,否则您将继续遍历第一个元素满足谓词的列表,并且永远不会摆脱它。
你可能想要的是与span
交错的break
:
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
我想'(pre2,suf2)= span f list'应该是'(pre2,suf2)= span f suf1'? –
@AlexeyRomanov:正确。感谢您的发现。 –
感谢@Al –
这将是因为这将不断添加一个空列表到最后。
你可以看到这是,如果你把值的一些任意数量从无限集合:
*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]
芹苴我想这d更像splitAt
什么的,不是吗?
因为'break'不*不保证*进展? –
模式是详尽的,否则你会得到一个运行时异常(或编译时警告)。这里的问题是无限递归而不是详尽无遗。 – chi
你正在寻找的词是'生成的'。另外,考虑'splitWith(const False)'这个非常简单的例子。 – user2407038