9

私は、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生成するのに、2つの別々の引数として生成するのはなぜですか?

4

4 に答える 4

5

試す:

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

map渡された値を自動的に分解しています。次の質問は、なぜそれがこの脱構築を行っているのか、そうでselectはないのかということです。

配列のRdocページに記載されているソースを見ると、それらは実質的に同一であり、生成selectされた値をテストするという点でのみ異なります。他の場所で何かが起こっているに違いありません。

Rubiniusのソース(主にCよりもRubyの方が優れているため)を見るとmap(からのエイリアスcollect)、次のように表示されます。

each do |*o|

したがって、途中で引数が飛び散りますが、select(からのエイリアスfind_all)は次のことを行いません。

each do

繰り返しになりますが、理由に関する設計上の決定は私を超えています。あなたはそれを書いた人を見つける必要があるでしょう、多分マッツに聞いてください:)


Rubiniusのソースをもう一度見て、map実際のスプラットeach 何度も追加する必要があります。歩留まりのスプラットだけが必要なのに、なぜ両方を実行するyieldのかわかりません。

  each do |*o|
    ary << yield(*o)
  end

一方、そうではありselectません。

each do
  o = Rubinius.single_block_arg
  ary << o if yield(o)
end
于 2013-01-24T03:17:31.887 に答える
4

MRIの情報源によると、スプラットで使用されているイテレータはselect引数が入ってくるように見えますが、そうで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配列値に変換するためにマクロが使用されていると確信しています(後者の引数が黙って無視されるタプルとは対照的です)。とはいえ、なぜこのように設計されたのかはわかりません。

于 2013-01-24T03:24:54.633 に答える
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);
}

見る?引数は、 array.crb_ary_new4で定義されているによってパックされます。

于 2013-01-24T03:36:07.023 に答える
3

これまでの談話から、ソースコードを分析できるということになりますが、その理由はわかりません。Rubyコアチームは比較的応答性が高いです。http://bugs.ruby-lang.org/issues/でサインインし、そこにバグレポートを投稿することをお勧めします。彼らは確かにせいぜい数週間以内にこの問題を見るでしょう、そしてあなたはおそらくそれがRubyの次のマイナーバージョンで修正されることを期待することができます。(つまり、物事を現状のまま維持するための設計上の根拠が不明な場合を除きます。)

于 2013-01-24T04:36:42.720 に答える