假设我有一个已排序的数组,例如:
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
这样工作吗?
注意:我发了implementation-agnostic,因为我找不到任何描述我关注的标签。如果您了解它,请随意将标签更改为更好的内容。
答案 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调用的。
本质上,上述逻辑保留了枚举对象发射对象的顺序。