このコードから、2つのメソッドcollect
とeach
の違いがわかりません。
a = ["L","Z","J"].collect{|x| puts x.succ} #=> M AA K
print a.class #=> Array
b = ["L","Z","J"].each{|x| puts x.succ} #=> M AA K
print b.class #=> Array
Array#each
は配列を受け取り、指定されたブロックをすべてのアイテムに適用します。配列には影響せず、新しいオブジェクトを作成します。これは単にアイテムをループする方法です。また、自己を返します。
arr=[1,2,3,4]
arr.each {|x| puts x*2}
2,4,6,8を出力し、[1,2,3,4]を返します
Array#collect
はArray#map
と同じであり、指定されたコードブロックをすべてのアイテムに適用し、新しい配列を返します。 'シーケンスの各要素を新しいフォームに投影する'
arr.collect {|x| x*2}
[2,4,6,8]を返します
そしてあなたのコードで
a = ["L","Z","J"].collect{|x| puts x.succ} #=> M AA K
aは配列ですが、実際にはNilの[nil、nil、nil]の配列です。なぜなら、puts x.succ
はnil
を返すからです(たとえM AA Kを出力しても)。
そして
b = ["L","Z","J"].each{|x| puts x.succ} #=> M AA K
また、配列です。しかし、自己を返すため、その値は["L"、 "Z"、 "J"]です。
Array#each
は、各要素を取得してブロックに挿入し、元の配列を返します。 Array#collect
は各要素を取り、それを新しい配列に入れて返します:
[1, 2, 3].each { |x| x + 1 } #=> [1, 2, 3]
[1, 2, 3].collect { |x| x + 1 } #=> [2, 3, 4]
each
は、配列を繰り返し処理し、各繰り返しで必要な処理を実行する場合に使用します。ほとんどの(必須の)言語では、これは、リストを処理する必要があるときにプログラマが到達する「1つのサイズですべてに対応する」ハンマーです。
より機能的な言語の場合、他の方法で実行できない場合にのみ、この種の汎用反復を実行します。ほとんどの場合、mapまたはreduceのいずれかがより適切です(Rubyで収集および注入)
collect
は、ある配列を別の配列に変換する場合に使用します
inject
は、配列を単一の値に変換する場合に使用します
docs ...によると、2つのソースコードスニペットがあります。
_VALUE
rb_ary_each(VALUE ary)
{
long i;
RETURN_ENUMERATOR(ary, 0, 0);
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(RARRAY_PTR(ary)[i]);
}
return ary;
}
# .... .... .... .... .... .... .... .... .... .... .... ....
static VALUE
rb_ary_collect(VALUE ary)
{
long i;
VALUE collect;
RETURN_ENUMERATOR(ary, 0, 0);
collect = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_ary_Push(collect, rb_yield(RARRAY_PTR(ary)[i]));
}
return collect;
}
_
rb_yield()
は、ブロックによって返された値を返します( メタプログラミングに関するこのブログ投稿も参照 )。
したがって、each
justyieldsと元の配列を返しますが、collect
は新しい配列を作成し、その結果をプッシュしますそれにブロックします。次に、この新しい配列を返します。
違いはそれが返すものです。上記の例ではa == [nil,nil,nil]
(puts x.succの値)b == ["L", "Z", "J"]
(元の配列)
Ruby-docから、collectは次のことを行います。
Selfの各要素に対してブロックを1回呼び出します。ブロックによって返された値を含む新しい配列を作成します。
それぞれが常に元の配列を返します。理にかなっていますか?
私はそれを理解する簡単な方法は次のようになると思います:
nums = [1, 1, 2, 3, 5]
square = nums.each { |num| num ** 2 } # => [1, 1, 2, 3, 5]
代わりに、collectを使用する場合:
square = nums.collect { |num| num ** 2 } # => [1, 1, 4, 9, 25]
さらに、.collect!
は元の配列を変更します。
それぞれは、Enumerableモジュールを含むすべてのクラスによって定義されるメソッドです。 Object.each
はEnumerable::Enumerator
オブジェクトを返します。これは、オブジェクトを反復処理するために他のEnumerableメソッドが使用するものです。各クラスのeach
メソッドの動作は異なります。
Arrayクラスでは、ブロックがeach
に渡されると、各要素でブロックのステートメントを実行しますが、最終的にはselfを返します。これは、配列が不要な場合に便利ですが、配列から要素を選択し、他のメソッドへの引数として使用します。 inspect
およびmap
は、各要素でのブロックの実行の戻り値を含む新しい配列を返します。 map!
およびcollect!
を使用して、元の配列で操作を実行できます。