Ruby 链接产生多个参数的枚举数

Ruby 链接产生多个参数的枚举数,ruby,block,enumerators,Ruby,Block,Enumerators,我试图弄清楚Ruby如何处理产生多个参数的链式枚举数。请看以下片段: a = ['a', 'b', 'c'] a.each_with_index.select{|pr| p pr} # prints: # ["a", 0] # ["b", 1] # ["c", 2] a.each_with_index.map{|pr| p pr} # prints: # "a" # "b" # "c" 为什么select将参数作为数组生成,而map将它们作为两个独立的参数生成?请尝试: a.each_wi

我试图弄清楚Ruby如何处理产生多个参数的链式枚举数。请看以下片段:

a = ['a', 'b', 'c']

a.each_with_index.select{|pr| p pr}
# prints:
# ["a", 0]
# ["b", 1]
# ["c", 2]

a.each_with_index.map{|pr| p pr}
# prints:
# "a"
# "b"
# "c"
为什么select将参数作为数组生成,而map将它们作为两个独立的参数生成?

请尝试:

a.each_with_index.map{|pr,last| p "pr: #{pr} last: #{last}"}
映射将自动解构传递给它的值。下一个问题是,它为什么要进行这种解构,而不是选择

如果您看到它们实际上是相同的,那么select的不同之处在于它对生成的值进行测试。一定是别处发生了什么事

如果我们看一下,主要是因为我使用Ruby比使用C更好;对于“收集”中的地图别名,它向我们显示:

each do |*o|
因此,它在传递过程中散播了参数,而来自find_all的别名没有:

each do
同样,关于为什么要这样做的设计决策我无法理解。你得找出是谁写的,也许问问Matz:

我应该补充一点,再次查看Rubinius源代码,映射每个和产量上的实际splat,我不明白为什么在只需要产量splat的情况下,您会同时这样做:

  each do |*o|
    ary << yield(*o)
  end
根据,select中使用的迭代器似乎会将其参数放大,但map不会,而是将其解包传递;后一种情况下的块会自动忽略其他参数

选择中使用的迭代器:

映射中使用的迭代器:

我非常确定ENUM_WANT_SVALUE宏用于将传递到块中的值转换为splat数组值,而不是元组,后者的参数被默默忽略。也就是说,我不知道为什么它是这样设计的。

让我们来看看MRI的来源。正如@PlatinumAzure所说,神奇发生在ENUM_WANT_SVALUE中:

我们可以发现这个宏实际上是:do{i=rb_enum_values_packargc,argv;}while0

让我们继续深入了解rb_enum_values_pack函数:

VALUE
rb_enum_values_pack(int argc, VALUE *argv)
{
    if (argc == 0) return Qnil;
    if (argc == 1) return argv[0];
    return rb_ary_new4(argc, argv);
}

看到了吗?这些参数由rb_ary_new4压缩,它在中定义。

从到目前为止的论述来看,我们可以分析源代码,但我们不知道原因。Ruby核心团队相对来说反应非常迅速。我建议您登录并在那里发布错误报告。他们肯定最多会在几周内看到这个问题,您可能会期望在Ruby的下一个小版本中更正它。也就是说,除非有我们不知道的设计原理来保持事物的原样。

非常有趣。这是一个非常罕见的高质量Ruby问题,最近有新来者提出。你是一个聪明人。我已经把这个问题的邮件发给了ruby core,等我收到回复后,我会在这里更新。最好的办法就是注册,我也把这个贴出来作为答案。我就是这么想的。我仍然对原因感兴趣——以及是否有关于哪些方法使用哪种技术的文档。你知道与Matz/核心ruby开发者取得联系的最佳方式是什么吗?我会将此作为常规答案发布。@BenWeissmann那么Boris给出的答案是最有帮助的。我建议现在就找出哪种方法使用哪种技术,或者尝试使用IRB中的模式进行构造,例如do | a,b |…或者查看Rubinius源,因为它应该与MRI实现匹配,并查看屈服是否需要一个splat。我上面给出的链接将把您带到正确的位置,kernel/common/xxx中的任何一个都将是核心。您可以随时询问Freenode上Rubinius中的Rubinius贡献者,因为他们重新实现了它。
static VALUE
find_all_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
    ENUM_WANT_SVALUE();

    if (RTEST(rb_yield(i))) {
        rb_ary_push(ary, i);
    }
    return Qnil;
}
static VALUE
collect_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
    rb_ary_push(ary, enum_yield(argc, argv));

    return Qnil;
}
static VALUE
find_all_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
    ENUM_WANT_SVALUE();

    if (RTEST(rb_yield(i))) {
        rb_ary_push(ary, i);
    }
    return Qnil;
}
VALUE
rb_enum_values_pack(int argc, VALUE *argv)
{
    if (argc == 0) return Qnil;
    if (argc == 1) return argv[0];
    return rb_ary_new4(argc, argv);
}