在第一次出现元素时拆分列表

时间:2016-10-28 02:14:38

标签: haskell

我想知道是否有一个函数可以让我在第一次出现元素时拆分列表,不包括该元素。基本上,假设:

listT= [1,3,2,5,6,3,2,6]

然后我想定义(或者不是,如果它已经存在),一个函数splitAtFirst,以便

splitAtFirst 2 listT = [[1,3],[5,6,3,2,6]]

到目前为止我的尝试看起来像这样:

splitAtfirst::(Eq a)=> a->[a]->[[a]]
splitAtfirst _ []=[]
splitAtfirst a (x:xs) 
   |a==x        = [xs]
   |otherwise   = [x]: splitAtfirst a (xs) 

然而,我得到了

>splitAtfirst 2 [1,3,2,5,6,3,2,6]
>[[1],[3],[5,6,3,2,6]]

我知道问题发生的原因,但到目前为止我还没有找到一个好的解决方案

我感谢任何帮助!

编辑:这是一个辅助函数,只有在检查到elem在列表中后才会被调用,因此处理不存在时的情况并不是必需的。感谢您的帮助

2 个答案:

答案 0 :(得分:7)

您已提交错误的结果类型,这会阻止类型检查程序尽可能地帮助您。你说你想要

splitAtFirst 2 listT = [[1,3],[5,6,3,2,6]]

这会引导你

splitAtfirst::(Eq a)=> a->[a]->[[a]]

允许将列表拆分为任意多个列表。

但实际上你想将列表拆分为 two 。如果我问splitAtFirst 2 [1,3]之类的问题,那么你在问题中没有提到的一点就是你想要的结果。有三种选择似乎是合理的:

splitAtFirst :: Eq a => a -> [a] -> Maybe ([a],[a])
-- splitAtFirst 2 [1,3] = Nothing

splitAtFirst :: Eq a => a -> [a] -> ([a],[a])
-- splitAtFirst 2 [1,3] = ([1,3],[])

splitAtFirst :: Eq a => a -> [a] -> ([a], Maybe [a])
-- splitAtFirst 2 [1,3] = ([1,3],Nothing)

请注意,我没有建议

-- splitAtFirst 2 [1,3] = ([], [1,3])

原因与懒惰和效率有关 - 你能弄明白为什么吗?第一个Maybe版本也有这个潜在的问题,但它通过更有用来弥补它。

一旦您提交了其中一个选项,您可能会发现更容易实现正确的实施。

答案 1 :(得分:6)

由于dfeuer已经指出了修复实施的方法,我将简单介绍现成的解决方案:

splitAtFirst :: Eq a => a -> [a] -> ([a], [a])
splitAtFirst x = fmap (drop 1) . break (x ==)

这个splitAtFirst实现了dfeuer答案中三个建议的第二个选项。关于每个组件的一些注释:

  • (x ==)是一个用x测试相等性的函数,返回Bool

  • break使用布尔测试(此处为(x ==))将列表分成两部分,第二个列表以通过测试的第一个元素开头。这两个列表作为一对返回(请参阅dfeuer的答案,原因是这样)。

  • drop 1如果列表的第一个元素不为空,则删除该列表的第一个元素(如果列表为空,则将其单独留下)。

  • fmap需要花费更长的时间才能正确解释,所以我只会说fmap f对一对应用(例如break的结果)适用于函数f到该对的第二个元素。在我们的例子中,fmap (drop 1)将删除第二个列表的第一个元素 - 即分隔符。 (要了解有关fmap的更多信息,请搜索关键字“functor”,或者只是尝试在列表或Maybe - 值上使用它,看看会发生什么。)