将非连续列表递归排序为连续列表的列表
最近我一直在尝试学习一些函数式编程(使用 Haskell 和 Erlang),我总是对人们在能够递归思考并了解工具时能够想出的简洁解决方案感到惊讶。
我想要一个函数将排序的、唯一的、非连续整数的列表转换为连续列表的列表,即:
[1,2,3,6,7,8,10,11]
to:
[[1,2,3], [6,7,8], [10,11]
这是我在 Haskell 中能想到的最好的(两个函数)::
make_ranges :: [[Int]] -> [Int] -> [[Int]]
make_ranges ranges [] = ranges
make_ranges [] (x:xs)
| null xs = [[x]]
| otherwise = make_ranges [[x]] xs
make_ranges ranges (x:xs)
| (last (last ranges)) + 1 == x =
make_ranges ((init ranges) ++ [(last ranges ++ [x])]) xs
| otherwise = make_ranges (ranges ++ [[x]]) xs
rangify :: [Int] -> [[Int]]
rangify lst = make_ranges [] lst
可能有点主观的,但我有兴趣在 Erlang 或 Haskell 中看到更好、更优雅的解决方案(还有其他函数式语言,但我可能不理解它。)否则,请修复我蹩脚的初学者的 Haskell 风格!
I've been trying to learn a bit of functional programming (with Haskell & Erlang) lately and I'm always amazed at the succinct solutions people can come up with when they can think recursively and know the tools.
I want a function to convert a list of sorted, unique, non-contiguous integers into a list of contiguous lists, i.e:
[1,2,3,6,7,8,10,11]
to:
[[1,2,3], [6,7,8], [10,11]
This was the best I could come up with in Haskell (two functions)::
make_ranges :: [[Int]] -> [Int] -> [[Int]]
make_ranges ranges [] = ranges
make_ranges [] (x:xs)
| null xs = [[x]]
| otherwise = make_ranges [[x]] xs
make_ranges ranges (x:xs)
| (last (last ranges)) + 1 == x =
make_ranges ((init ranges) ++ [(last ranges ++ [x])]) xs
| otherwise = make_ranges (ranges ++ [[x]]) xs
rangify :: [Int] -> [[Int]]
rangify lst = make_ranges [] lst
It might be a bit subjective but I'd be interested to see a better, more elegant, solution to this in either Erlang or Haskell (other functional languages too but I might not understand it.) Otherwise, points for just fixing my crappy beginner's Haskell style!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
在我看来,最直接的方法是foldr:
或者更简洁地说:
但是等等,还有更多!
通过将保护函数转变为参数,您可以将更多有趣的东西分组,而不仅仅是+1序列。
这个特定功能的实用性值得怀疑……但是很有趣!
Most straightforward way in my mind is a foldr:
Or, more concisely:
But wait, there's more!
By turning the guard function into a parameter, you can group more interesting things than just +1 sequences.
The utility of this particular function is questionable...but fun!
我不敢相信我得到了最短的解决方案。我知道这不是高尔夫代码,但我认为它仍然具有很好的可读性:
或者 pointfree
BTW,
groupWith snd
可以替换为groupBy (\ab -> snd a == snd b )
如果您更喜欢Data.List
而不是GHC.Exts
[编辑]
顺便说一句:有没有更好的(\ab -> (ba, b)) 的方法比
(curry $ (,) <$> ((-) <$ > snd <*> fst) <*> snd) ?
[编辑 2]
是的,我忘记了
(,)
是一个函子。所以这是混淆版本:欢迎提出建议......
I can't believe I got the shortest solution. I know this is no code golf, but I think it is still quite readable:
or pointfree
BTW,
groupWith snd
can be replaced withgroupBy (\a b -> snd a == snd b)
if you preferData.List
overGHC.Exts
[Edit]
BTW: Is there a nicer way to get rid of the lambda
(\a b -> (b-a, b))
than(curry $ (,) <$> ((-) <$> snd <*> fst) <*> snd)
?[Edit 2]
Yeah, I forgot
(,)
is a functor. So here is the obfuscated version:Suggestions are welcome...
至于如何想出这样的事情:我从
zipWith f xs (tail xs)
开始,当您想要对列表的连续元素执行某些操作时,这是一个常见的习惯用法。同样,zip
会包含有关列表的信息,然后对其进行操作 (groupBy
)。剩下的就是水管了。然后,当然,您可以通过 @pl 和 get: 提供它
,根据我的权威定义,由于
Mondad ((->) a)
,这是邪恶的。 两次,甚至。数据流过于曲折,无法以任何合理的方式进行布局。zipaptail
是一个阿兹特克神,阿兹特克神是不能被打扰的。As to how to come up with such a thing: I started with the
zipWith f xs (tail xs)
, which is a common idiom when you want to do something on consecutive elements of a list. Likewise iszip
ping up a list with information about the list, and then acting (groupBy
) upon it. The rest is plumbing.Then, of course, you can feed it through @pl and get:
, which, by my authoritative definition, is evil due to
Mondad ((->) a)
. Twice, even. The data flow is meandering too much to lay it out in any sensible way.zipaptail
is an Aztec god, and Aztec gods aren't to be messed with.Erlang 中的另一个版本:
Another version in Erlang:
尝试重用标准函数。
或者,遵循(混合)建议使用
unfoldr
,停止滥用groupBy
,并在无关紧要的时候乐于使用部分函数:Try reusing standard functions.
Or, following (mixed) advice to use
unfoldr
, stop abusinggroupBy
, and be happy using partial functions when it doesn't matter:Erlang 使用foldr:
Erlang using foldr:
这是我的 v0.1,我可能可以让它变得更好:
我会尽力让它变得更好。我想编辑即将到来。
This is my v0.1 and I can probably make it better:
And I will try and make it better. Edits coming I think.
作为比较,下面是 Erlang 中的实现:
As a comparison, here's an implementation in Erlang:
标准的同态递归方案不在 Haskell 的 Data.List 模块中,尽管我认为它应该是。这是使用同态的解决方案,因为您正在从列表构建列表列表,所以缺点有点棘手:
同态是一般递归或带有前瞻的折叠:
The standard paramorphism recursion scheme isn't in Haskell's Data.List module, though I think it should be. Here's a solution using a paramorphism, because you are building a list-of-lists from a list, the cons-ing is a little tricksy:
Paramorphism is general recursion or a fold with lookahead:
在 Erlang 中它可以非常清晰和简单:
编辑:出于好奇,我比较了我的和 Lukas< /a> 的版本 和我的在测试集上的本地或字节码版本中似乎快了大约 10% 我通过
lists:usort([random:uniform(1000000)||_<-lists:seq(1,1000000) 生成的内容])
在我的笔记本上的 R14B01 64b 版本上。 (测试集长度为 669462,已划分为 232451 个子列表。)Edit2:另一个测试数据
lists:usort([random:uniform(1000000)||_<-lists:seq( 1,10000000)])
,长度999963和38个分区在本机代码中产生更大的差异。我的版本只用了不到一半的时间就完成了。字节码版本仅快 20% 左右。Edit3:一些微优化提供了额外的性能,但会导致代码更难看且更难维护:
It can be pretty clear and simple in the Erlang:
Edit: Just for curiosity I have compared mine and Lukas's version and mine seems about 10% faster either in native either in bytecode version on testing set what I generated by
lists:usort([random:uniform(1000000)||_<-lists:seq(1,1000000)])
on R14B01 64b version at mine notebook. (Testing set is 669462 long and has been partitioned to 232451 sublists.)Edit2: Another test data
lists:usort([random:uniform(1000000)||_<-lists:seq(1,10000000)])
, length 999963 and 38 partitions makes bigger diference in native code. Mine version finish in less than half of time. Bytecode version is only about 20% faster.Edit3: Some microoptimizations which provides additional performance but leads to more ugly and less maintainable code:
这是一个 haskell 菜鸟的尝试
Here's an attempt from a haskell noob