为什么这段代码中的模式不详尽?

时间:2017-07-06 11:28:45

标签: haskell functional-programming

splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith f [] = []
splitWith f list = pre : (splitWith f suf)
    where (pre, suf) = break f list

此函数应根据谓词拆分列表。但我得到了无限的递归。

2 个答案:

答案 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将简单地将列表拆分为空列表和原始列表。结果,可以说模式没有进展。除非所有元素都不满足谓词,否则你将继续迭代第一个元素满足谓词的列表,并且永远不会删除它。

您可能希望将breakspan

交错
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或其他东西,不是吗?