Ruby中的sort
稳定吗?也就是说,对于与sort
并列的元素,它们之间的相对顺序是否保留了原始顺序?例如,给定:
a = [
{id: :a, int: 3},
{id: :b, int: 1},
{id: :c, int: 2},
{id: :d, int: 0},
{id: :e, int: 1},
{id: :f, int: 0},
{id: :g, int: 1},
{id: :h, int: 2},
]
是否能保证我们总能得到
a.sort_by{|h| h[:int]}
以下
[
{id: :d, int: 0},
{id: :f, int: 0},
{id: :b, int: 1},
{id: :e, int: 1},
{id: :g, int: 1},
{id: :c, int: 2},
{id: :h, int: 2},
{id: :a, int: 3},
]
具有:id
值的元素:d
、:f
之间,:b
、:e
、:g
之间,以及:c
、:h
之间的相对顺序没有任何变化?如果是这种情况,文档中的哪个位置对此进行了描述?
这个问题可能与this question有关,也可能无关。
4条答案
按热度按时间5f0d552i1#
MRI的sort和sort_by都是不稳定的,前段时间有个request让它们稳定,但是被拒绝了,原因是:Ruby使用了in-place quicksort algorithm,如果不需要稳定性的话,in-place quicksort algorithm会表现得更好,注意你仍然可以从不稳定的方法实现稳定的方法:
zysjyyx42#
不,ruby的内置排序不稳定。
如果你想要稳定排序,这应该可以。如果你经常使用它,你可能需要为它创建一个方法。
基本上,它跟踪每个项的原始数组索引,并在
h[:int]
相同时将其用作平局决胜。更多信息,为好奇:
据我所知,在使用不稳定排序时,使用原始数组索引作为决胜局是保证稳定性的唯一方法,项目的实际属性(或其他数据)不会告诉您它们的原始顺序。
您的示例有些做作,因为
:id
键在原始数组中是升序排序的。你会希望结果中的:id
在决胜局时是递减的,如下所示:使用原始索引也可以处理这个问题。
更新日期:
Matz自己的建议(参见this page)也是类似的,并且可能比上面的建议稍微更有效:
rsaldnfx3#
对于Ruby的一些实现来说,sort是稳定的,但是你不应该依赖它,Ruby的sort的稳定性是由实现定义的。
文档内容
该文档指出,您不应该依赖于sort是否稳定:
不保证结果是稳定的。当两个元素的比较返回0时,元素的顺序是不可预测的。
注意,这并不是说排序是否稳定。它只是说排序不一定是稳定的。任何给定的Ruby实现都可能有一个稳定的排序,但仍然与文档一致。它也可能有一个不稳定的排序,或者随时改变排序是否稳定。
露比真正做的是
如果Ruby的排序是稳定的,那么这个测试代码输出
true
,否则输出false
:下面是我在Linux系统上安装的所有Rubies的结果:
我们可以看到JRuby是不稳定的,MRI 2.2之前在Linux上是不稳定的,MRI〉= 2.2.0是稳定的(同样在Linux上)。
尽管上面的结果表明sort在Linux上的MRI 2.4.1中是稳定的,但在Windows上同样的版本是不稳定的:
为什么MRI的排序在Linux上稳定,而在Windows上不稳定?
即使在一个Ruby实现的单一版本中,排序算法也可以改变。MRI可以使用至少三种不同的排序。在util.c中,排序例程是在编译时使用一系列#ifdef选择的。看起来MRI可以使用至少两个不同库中的排序。它也有自己的实现。
你应该怎么做?
因为排序可能是稳定的,但不能保证是稳定的,所以不要编写依赖于Ruby排序稳定的代码,因为在不同的版本、实现或平台上使用时,代码可能会崩溃。
7kjnsjlb4#
就我个人而言,我不会指望这个。不如这样做: