给出如下列表:
[1, 2, 2, 6, 7, 8, 10, 11, 12, 15]
将其拆分为温和增加范围(可能相等):
[[1, 2, 2], [6, 7, 8], [10, 11, 12], [15]]
我尝试使用递归方法:
splitRanges [] = [[]]
splitRanges (x:y:xs)
| x `elem` [y, y + 1] = [x, y] : splitRanges xs
| otherwise = xs
因此,如果项目在融合它们之后是小于或等于项目的项目。但它说我试图建立一个无限类型:
Occurs check: cannot construct the infinite type: a0 = [a0]
Expected type: [[a0]]
Actual type: [a0]
但是[单调的事实]与列表的拆分方式有什么关系呢?
严格增加会产生不同的结果。
或者你真的想说点别的吗?
我希望我不是。
列表总是单调吗?
不,拆分单调列表意味着将其分成一个子列表。
如果没有,那应该如何影响结果?
如果不是单调的话,你会有很多子列表。
总是棕色成三个一组吗?
不,这些组可能包含n个元素。
更多例子会很好
splitRanges [1, 3] == [[1], [3]]
splitRanges [1, 2, 5] == [[1, 2], [3]]
splitRanges [0, 0, 1] == [[0, 0, 1]]
splitRanges [1, 5, 7, 9] == [[1], [5], [7], [9]]
我很欣赏暗示而不是完整的答案,因为我想提高自己,复制粘贴并不是改进。
答案 0 :(得分:2)
尝试将问题分解为更易于管理的部分。
首先,您如何从列表的开头分割出一个平淡增长的范围?让我猜猜应该是splitOne :: [Integer] -> ([Integer], [Integer])
。
其次,如何重复将splitOne应用于剩余列表?尝试使用splitOne实现splitMany :: [Integer] -> [[Integer]]
。
对于splitOne,您应该尝试找到什么?第一个分裂的位置。什么是"分拆职位"?让我们说清楚。
split 0 1 2 3 4 …
list [ | x1, | x2, | x3, | x4, | x5, …]
因此,0处的拆分为([], [x1,x2,x3,x4,x5,…])
,3处的拆分为([x1,x2,x3],[x4,x5,…])
。您可以在拆分位置和拆分列表之间看到什么关系?
如何确定列表的第一个拆分位置?可以说它实现为firstSplitPos :: [Integer] -> Integer
。空列表的第一个拆分位置是什么?
现在可以使用firstSplitPos实现splitOne吗?
-- What are the adjacencies for:
-- 1) empty lists?
-- 2) lists with one element?
-- 3) lists with more than one element?
--
-- Bonus: rewrite in point-free form using <*>
--
adjacencies :: [a] -> [(a,a)]
adjacencies xxs = zip xxs (drop 1 xxs)
-- Bonus: rewrite in point-free form
--
withIndices :: [a] -> [(Int,a)]
withIndices xxs = zip [0..] xxs
-- This is the most involved part of the answer. Pay close
-- attention to:
-- 1) empty lists
-- 2) lists with one element
-- 3) lists which are a blandly increasing sequence
--
firstSplitPos :: (Eq a, Num a) => [a] -> Int
firstSplitPos xxs = maybe (length xxs) pos (find q searchList)
where q (_,(a,b)) = a /= b && a + 1 /= b
searchList = withIndices (adjacencies xxs)
-- Why is the split position one more than the index?
pos (i,_) = i + 1
--
-- Bonus: rewrite in point-free form using <*>
--
splitOne :: (Eq a, Num a) => [a] -> ([a],[a])
splitOne xxs = splitAt (firstSplitPos xxs) xxs
splitMany :: (Eq a, Num a) => [a] -> [[a]]
-- What happens if we remove the case for []?
splitMany [] = []
splitMany xxs = let (l, r) = splitOne xxs in l : splitMany r
这是我对Carsten's solution的解释。它已经很简洁,但我选择了一个不使用2元组的变体。
我们知道Haskell列表是归纳定义的。为了证明这一点,我们可以定义一个等效的数据类型。
data List a = Cons a (List a) -- Cons = (:)
| Nil -- Nil = []
然后问一个问题:我们可以在列表中使用归纳法来解决问题吗?如果是这样,我们只需要解决两种情况:Cons和Nil。 foldr
的类型签名向我们展示了:
foldr :: (a -> b -> b) -- Cons case
-> b -- Nil case
-> [a] -- The list
-> b -- The result
如果列表是Nil怎么办?然后,唯一温和增加的序列是空序列。因此:
nilCase = [[]]
我们可能需要nilCase = []
代替,因为这似乎也是合理的 - 即没有平淡增长的序列。
现在你需要一些想象力。在Cons案例中,我们一次只能查看一个新元素。使用这个新元素,我们可以决定它是属于右邻序列还是开始一个新序列。
右边相邻是什么意思?在[5,4,1,2,2,7]
中,1
属于右邻序列[2,2]
。
这看起来怎么样?
-- The rest of the list is empty
consCase new [] = [new] : []
-- The right-adjacent sequence is empty
consCase new ([]:ss) = [new] : ss
-- The right-adjacent sequence is non-empty
-- Why `new + 1 == x` and not `new == x + 1`?
consCase new sss@(xxs@(x:_):ss)
| new == x || new + 1 == x = (new:xxs):ss
| otherwise = [new]:sss
现在我们解决了Nil案和Cons案,我们已经完成了!
splitRanges = foldr consCase nilCase
答案 1 :(得分:1)
我希望你不介意破坏它的一部分,但是当评论正在讨论你想要的东西时(我希望我已经得到它)也许你对另一种可能的解决方案感兴趣?
我不想破坏这一切,但我认为你可以轻松解决这个问题:
blandly :: (Ord a, Num a) => [a] -> [[a]]
blandly = g . foldr f ([],[])
where f x ([],xss) = ([x],xss)
f x (y:ys,xss)
| abs (x-y) <= 1 = undefined
| otherwise = undefined
g (ys,xss) = undefined
你只需要填写undefined
个洞
这个想法只是从右边折叠列表,在元组的第一项中累积你的内部列表,只要元素不是很远;如果它们是:将它推到第二个项目。
如果操作正确,它将产生:
λ> blandly [1,3]
[[1],[3]]
λ> blandly [1,2,5]
[[1,2],[5]]
λ> blandly [0,0,1]
[[0,0,1]]
λ> blandly [1,5,7,9]
[[1],[5],[7],[9]]
这似乎是你想要的
1小时后 - 我想我可以发布我的解决方案 - 如果你不想被宠坏就停止阅读
blandly :: (Ord a, Num a) => [a] -> [[a]]
blandly = uncurry (:) . foldr f ([],[])
where f x ([],xs) = ([x],xs)
f x (y:ys,xs)
| abs (x-y) <= 1 = (x:y:ys,xs)
| otherwise = ([x],(y:ys):xs)
也许我在这里有一点误会(示例没有指明) - 但如果你只想要单调增加内部列表,你只需要更改abs
部分:
blandly :: (Ord a, Num a) => [a] -> [[a]]
blandly = uncurry (:) . foldr f ([],[])
where f x ([],xss) = ([x],xss)
f x (y:ys,xss)
| 0 <= y-x
&& y-x <= 1 = (x:y:ys,xss)
| otherwise = ([x],(y:ys):xss)
答案 2 :(得分:1)
编写函数来获取谓词,而不是将拆分条件写入函数本身,这将是有用的和惯用的:
splitBy2 :: (a -> a -> Bool) -> [a] -> [[a]]
splitBy2 ok xs = snd $ f xs [] []
where f (a:b:xs) acc_list acc_out_lists | ok a b = ...