2017-07-06 71 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什麼的,不是嗎?

相關問題