[英]How do you convert a list of numbers into a list of ranges in haskell?
Say you have a list of numbers, [1,2,3,5,6,7,8,9,11,12,15,16,17]
假设你有一个数字列表,
[1,2,3,5,6,7,8,9,11,12,15,16,17]
and you want a function that takes that as an input and returns something like并且您想要一个将其作为输入并返回类似内容的函数
[[1,3],[5,9],[11,12],[15,17]]
or alternatively maybe [(1,3), (5,9), (11,12), (15,17)]
[[1,3],[5,9],[11,12],[15,17]]
或者[(1,3), (5,9), (11,12), (15,17)]
how would this be done?这将如何完成? all of the solutions i've found online are very very long and quite convoluted, when this seems like such an easy problem for a functional language like haskell
我在网上找到的所有解决方案都非常长而且非常复杂,这对于像 haskell 这样的函数式语言来说似乎是一个简单的问题
So we have a list of numbers,所以我们有一个数字列表,
xs = [1,2,3,5,6,7,8,9,11,12,14,16,17] -- 14 sic!
We turn it into a list of segments,我们把它变成一个段列表,
ys = [[x,x+1] | x <- xs]
-- [[1,2], [2,3], [3,4], [5,6], ..., [11,12], [12,13], [14,15], [16,17], [17,18] ]
we join the touching segments,我们加入感人的片段,
zs = foldr g [] ys
-- [[1,4], [5,10], [11,13], [14,15], [16,18]]
where
g [a,b] [] = [[a,b]]
g [a,b] r@([c,d]:t) | b==c = [a,d]:t
| otherwise = [a,b]:r
and we subtract 1 from each segment's ending value,我们从每个段的结束值中减去1 ,
ws = [[a,b-1] | [a,b] <- zs]
-- [[1,3], [5,9], [11,12], [14,14], [16,17]]
All in all we get总之我们得到
ranges :: (Num t, Eq t) => [t] -> [[t]]
ranges = map (\[a,b] -> [a,b-1]) . foldr g [] . map (\x -> [x,x+1])
where
g [a,b] [] = [[a,b]]
g [a,b] r@([c,d]:t) | b==c = [a,d]:t
| otherwise = [a,b]:r
Simple and clear.简单明了。
edit: or, to be properly lazy,编辑:或者,要适当地懒惰,
where
g [a,b] r = [a,x]:y
where
(x,y) = case r of ([c,d]:t) | b==c -> (d,t) -- delay forcing
_ -> (b,r)
update: as dfeuer notes, (a,a)
type is better than [a,a]
.更新:正如dfeuer 所指出的,
(a,a)
类型比[a,a]
更好。 Wherever [P,Q]
appears in this code, replace it with (P,Q)
.无论
[P,Q]
出现在此代码中的何处,都将其替换为(P,Q)
。 This will improve the code, with zero cost to readability.这将改进代码,零成本的可读性。
So one might do it like the idea from @Will Ness on the stateful folding or mine under the same answer .因此,人们可能会像@Will Ness关于有状态折叠的想法那样做,或者在相同的答案下进行我的想法。 All explanations are to be found there.
所有的解释都可以在那里找到。 Besides, if you get curious and want to read more about it then have a look at Haskell Continuation Passing Style page .
此外,如果您感到好奇并想阅读更多相关信息,请查看Haskell Continuation Passing Style 页面。 I am currently trying to gerealize this in such a way that we can have a variant of
foldr1
in a stateful manner.我目前正试图以这样一种方式来实现这一点,即我们可以以有状态的方式拥有
foldr1
的变体。 A foldS :: Foldable t => (a -> a -> b) -> ta -> b
. A
foldS :: Foldable t => (a -> a -> b) -> ta -> b
。 However this is still not general stateful folding.然而,这仍然不是一般的有状态折叠。 It's just tailored to this question.
它只是针对这个问题量身定制的。
ranges :: (Ord a, Num a) => [a] -> [[a]]
ranges xs = foldr go return xs $ []
where
go :: (Ord a, Num a) => a -> ([a] -> [[a]]) -> ([a] -> [[a]])
go c f = \ps -> let rrs@(r:rs) = f [c]
in case ps of
[] -> [c]:r:rs
[p] -> if p + 1 == c then rrs else [p]:(c:r):rs
*Main> ranges [1,2,3,5,6,7,8,9,11,12,15,16,17]
[[1,3],[5,9],[11,12],[15,17]]
I haven't had time to test any edge cases.我没有时间测试任何边缘情况。 All advices are welcome.
欢迎所有建议。
I would definitely prefer the alternative representation to the first one you give.与您提供的第一个表示相比,我绝对更喜欢替代表示。
ranges :: (Num a, Eq a) => [a] -> [(a,a)]
ranges [] = []
ranges (a : as) = ranges1 a as
-- | A version of 'ranges' for non-empty lists, where
-- the first element is supplied separately.
ranges1 :: (Num a, Eq a) => a -> [a] -> [(a,a)]
ranges1 a as = (a, b) : bs
where
-- Calculate the right endpoint and the rest of the
-- result lazily, when needed.
(b, bs) = finish a as
-- | This takes the left end of the current interval
-- and the rest of the list and produces the right endpoint of
-- that interval and the rest of the result.
finish :: (Num a, Eq a) => a -> [a] -> (a, [(a, a)])
finish l [] = (l, [])
finish l (x : xs)
| x == l + 1 = finish x xs
| otherwise = (l, ranges1 x xs)
To solve the Rosetta Code problem linked in the comment above, this isn't really quite an optimal representation.为了解决罗塞塔代码问题在上面的评论链接,这是不是真的挺的最佳表征。 I'll try to explain how to match the representation more precisely later.
稍后我将尝试解释如何更精确地匹配表示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.