web-dev-qa-db-ja.com

Rubyでmapとcollectの違いは?

私はこれをグーグルして、曖昧で矛盾する意見を得ました - Ruby/Railsの配列に対してmapを実行することとcollectを実行することの間に実際に違いはありますか?

docs は何も示唆していないようですが、おそらく方法やパフォーマンスに違いはありますか?

406
sscirrus

違いはありません。実際、mapはCでrb_ary_collectenum_collectとして実装されています(たとえば、配列のmapと他のenumの間に違いはありますが、mapcollectの間に違いはありません)。


Rubyにmapcollectの両方が存在するのはなぜですか?map関数には、さまざまな言語で多くの命名規則があります。 ウィキペディアに概要があります

Map関数は関数型プログラミング言語で作られましたが、今日では多くの手続き型、オブジェクト指向、そしてマルチパラダイム言語でもサポートされています(あるいは定義されるかもしれません):C++の標準テンプレートライブラリではC#(3.0)ではtransformと呼ばれています。 ■LINQライブラリ。これはSelectという拡張メソッドとして提供されています。 MapはPerl、Python、Rubyなどの高水準言語でもよく使われる操作です。この3つの言語すべてで、操作はmapと呼ばれます。 mapのcollectエイリアスも(Smalltalkの)Rubyで提供されています[強調]。 Common LISPは地図のような機能のファミリーを提供します。ここで説明した動作に対応するものはmapcarと呼ばれます(-carはCAR操作を使用したアクセスを示します)。

Rubyは、Smalltalkの世界から来たプログラマーに、くつろいだ雰囲気を提供するためのエイリアスを提供します。


配列とenumに異なる実装があるのはなぜですか?enumは一般化された繰り返し構造であり、Rubyが何を予測できるのかがわかりません。次の要素にすることができます(あなたは無限のenumを定義することができます。例として Prime を参照してください)。したがって、連続した各要素を取得するために関数を呼び出す必要があります(通常、これはeachメソッドになります)。

配列は最も一般的なコレクションなので、パフォーマンスを最適化するのが妥当です。 Rubyは配列がどのように動作するかについて多くのことを知っているので、eachを呼び出す必要はありませんが、単純な ポインタ操作 のみを使うことができます。これはかなり速いです。

ZipcountのようないくつかのArrayメソッドに対しても同様の最適化が存在します。

453
Jakub Hampl

私は言われました 彼らは同じです。

実際、それらはRuby-doc.orgの下の同じ場所に文書化されています。

http://www.Ruby-doc.org/core/classes/Array.html#M000249

  • ary.collect {| item |ブロック}→new_ary
  • ary.map {| item |ブロック}→new_ary
  • ary.collect→an_enumerator
  • ary.map→an_enumerator

Selfの各要素に対して1回blockを呼び出します。ブロックによって返された値を含む新しい配列を作成します。 Enumerable#collectも参照してください。
ブロックが指定されていない場合は、代わりに列挙子が返されます。

a = [ "a", "b", "c", "d" ]
a.collect {|x| x + "!" }   #=> ["a!", "b!", "c!", "d!"]
a                          #=> ["a", "b", "c", "d"]
47
OscarRyz

私はこの質問に答えてみるためにベンチマークテストをしました、そしてこの投稿を見つけたのでここに私の発見があります(他の回答とは少し異なります)

これがベンチマークコードです。

require 'benchmark'

h = { abc: 'hello', 'another_key' => 123, 4567 => 'third' }
a = 1..10
many = 500_000

Benchmark.bm do |b|
  GC.start

  b.report("hash keys collect") do
    many.times do
      h.keys.collect(&:to_s)
    end
  end

  GC.start

  b.report("hash keys map") do
    many.times do
      h.keys.map(&:to_s)
    end
  end

  GC.start

  b.report("array collect") do
    many.times do
      a.collect(&:to_s)
    end
  end

  GC.start

  b.report("array map") do
    many.times do
      a.map(&:to_s)
    end
  end
end

そして私が得た結果は次のとおりです。

                   user     system      total        real
hash keys collect  0.540000   0.000000   0.540000 (  0.570994)
hash keys map      0.500000   0.010000   0.510000 (  0.517126)
array collect      1.670000   0.020000   1.690000 (  1.731233)
array map          1.680000   0.020000   1.700000 (  1.744398) 

おそらくエイリアスは無料ではありませんか?

11
ktec

Rubyは、メソッドArray#mapをArray#collectにエイリアスします。それらは互換的に使用できます。 (ルビーモンク)

言い換えれば、同じソースコード:

               static VALUE
rb_ary_collect(VALUE ary)
{
long i;
VALUE collect;

RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
collect = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
    rb_ary_Push(collect, rb_yield(RARRAY_AREF(ary, i)));
}
return collect;
}

http://Ruby-doc.org/core-2.2.0/Array.html#method-i-map

7
jeton

collectメソッドおよびcollect!メソッドは、mapおよびmap!のエイリアスであるため、これらを交換して使用することができます。これを確認する簡単な方法は次のとおりです。

Array.instance_method(:map) == Array.instance_method(:collect)
 => true
4
BrunoFacca