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

时间:2013-01-24 02:22:35

标签: 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将它们作为两个单独的参数生成?

4 个答案:

答案 0 :(得分:5)

尝试:

a.each_with_index.map{|pr,last| p "pr: #{pr} last: #{last}"}

map会自动解构传递给它的值。接下来的问题是为什么它正在进行解构而select不是?

如果你看the source given on the Rdoc page for Array它们实际上是相同的,select只是区别在于它对产生的值进行了测试。必须在其他地方发生一些事情。

如果我们看map(主要是因为我对Ruby比C更好;)collect(来自each do |*o| 的别名),它会向我们展示:

find_all

所以它正在通过的方式展开论据,而Rubinius source(来自each do 的别名)不会:

map

再次,关于为什么的设计决定超出了我的范围。你必须找出是谁写的,也许问马茨:)


我应该添加,再次查看Rubinius来源,each yield上的 each do |*o| ary << yield(*o) end select上的实际splats,我不明白为什么你' d只在需要收益率时才做两件事:

each do
  o = Rubinius.single_block_arg
  ary << o if yield(o)
end

而{{1}}没有。

{{1}}

答案 1 :(得分:4)

根据the MRI source,似乎select中使用的迭代器splats其参数进入,但map没有并且将它们解压缩;在后一种情况下,该块会默默地忽略其他参数。

select中使用的迭代器:

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;
}

map中使用的迭代器:

static VALUE
collect_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
    rb_ary_push(ary, enum_yield(argc, argv));

    return Qnil;
}

我很确定ENUM_WANT_SVALUE()宏用于将传递给块的值转换为splat数组值(而不是以后面的参数静默忽略的元组)。也就是说,我不知道为什么会这样设计。

答案 2 :(得分:3)

让我们在enum.c中看到MRI来源。正如@PlatinumAzure所说,神奇发生在ENUM_WANT_SVALUE()

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;
}

我们可以发现这个宏实际上是:do {i = rb_enum_values_pack(argc, argv);}while(0)

所以让我们继续深入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打包,{{1}}定义。

答案 3 :(得分:3)

到目前为止,从话语中我们可以分析源代码,但我们不知道为什么。 Ruby核心团队的响应速度相对较快。我建议您登录http://bugs.ruby-lang.org/issues/并在那里发布错误报告。他们肯定会在几周内查看这个问题,你可能会期望在下一个次要版本的Ruby中纠正它。 (也就是说,除非我们不知道设计原理以保持原样。)