2017-07-06 62 views
1
splitWith :: (a -> Bool) -> [a] -> [[a]] 
splitWith f [] = [] 
splitWith f list = pre : (splitWith f suf) 
    where (pre, suf) = break f list 

该函数应根据谓词拆分列表。但是我得到了无限的递归。为什么这个代码中的模式没有详尽?

+1

因为'break'不*不保证*进展? –

+1

模式是详尽的,否则你会得到一个运行时异常(或编译时警告)。这里的问题是无限递归而不是详尽无遗。 – chi

+2

你正在寻找的词是'生成的'。另外,考虑'splitWith(const False)'这个非常简单的例子。 – user2407038

回答

4

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 
+1

我想'(pre2,suf2)= span f list'应该是'(pre2,suf2)= span f suf1'? –

+0

@AlexeyRomanov:正确。感谢您的发现。 –

+0

感谢@Al –

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] 

芹苴我想这d更像splitAt什么的,不是吗?

相关问题