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