我可以指望分区保留顺序吗?

时间:2015-07-30 17:36:31

标签: ruby implementation-agnostic

假设我有一个已排序的数组,例如:

myArray = [1, 2, 3, 4, 5, 6]

假设我打电话给Enumerable#partition

p myArray.partition(&:odd?)

输出必须始终如下吗?

[[1, 3, 5], [2, 4, 6]]

文件没有说明这一点;这就是它所说的:

分区{| obj |阻止}→[true_array,false_array]
分区→an_enumerator

返回两个数组,第一个包含枚举的元素,块的计算结果为true,第二个包含其余的数据
如果没有给出块,则返回枚举器。

但假设partition以这种方式运作似乎是合乎逻辑的。

通过测试Matz的解释器,似乎输出就像这样,并且它完全有意义,就像这样。但是,无论Ruby版本还是解释器,我都能依靠partition这样工作吗?

注意:我发了,因为我找不到任何描述我关注的标签。如果您了解它,请随意将标签更改为更好的内容。

2 个答案:

答案 0 :(得分:3)

不,你不能依赖订单。原因是并行性。

partition的传统串行实现将循环遍历数组的每个元素,按顺序一次评估一个块。每次对odd的调用都会返回,它会立即被推送到相应的true或false数组中。

现在想象一下利用多个CPU内核的实现。它仍按顺序遍历数组,但odd的每次调用都可能无序返回odd(myArray[2])可能会在odd(myArray[0])之前返回,从而导致[[3, 1, 5], [2, 4, 6]]

通过函数运行列表的partition列表处理习惯用法(大多数Enumerable)从并行处理中受益匪浅,而且现在大多数计算机都有多个核心。如果未来的Ruby实现利用了这一点,我不会感到惊讶。 Enumerable的API文档的编写者可能会仔细省略任何关于流程排序的提及,以使这种优化可能性保持开放。

答案 1 :(得分:1)

文档没有对此进行明确提及,但是从official code来看,它确实保留了顺序:

static VALUE
partition_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arys))
{
    struct MEMO *memo = MEMO_CAST(arys);
    VALUE ary;
    ENUM_WANT_SVALUE();

    if (RTEST(enum_yield(argc, i))) {
        ary = memo->v1;
    }
    else {
        ary = memo->v2;
    }
    rb_ary_push(ary, i);
    return Qnil;
}

此代码是从public interface调用的。

本质上,上述逻辑保留了枚举对象发射对象的顺序。