Разница между map и collect в Ruby?

я погуглил это и получил отрывочные / противоречивые мнения - есть ли на самом деле какая-то разница между выполнением map и collect на массиве в Ruby / Rails?

на docs не похоже, чтобы предложить какие-либо, но, возможно, есть различия в методе или производительности?

5 ответов


нет никакой разницы, на самом деле map реализовано в C как rb_ary_collect и enum_collect (напр. есть разница между map на массиве и на любом другом перечислении, но никакой разницы между map и collect).


почему map и collect существуют в Ruby? на map функция имеет много соглашений об именах на разных языках. Википедия предоставляет обзор:

функции карте возникший в функциональных языках программирования, но сегодня поддерживается (или может быть определен) во многих процедурных, объектно-ориентированных и многопарадигмальных языках: в стандартной библиотеке шаблонов C++он называется transform, в библиотеке LINQ C# (3.0), она предоставляется как метод расширения под названием Select. Map также часто используется в языках высокого уровня, таких как Perl, Python и Ruby; операция называется map на всех трех языках. A collect псевдоним карта также предоставляется в Ruby (от Smalltalk) [выделено мной]. Common Lisp предоставляет семейство картоподобных функций; та, которая соответствует описанному здесь поведению, называется mapcar (- автомобиль показывая доступ используя деятельность автомобиля).

Ruby предоставляет псевдоним для программистов из мира Smalltalk, чтобы чувствовать себя как дома.


почему существует другая реализация для массивов и перечислений? enum является обобщенной итерационная структура, что означает, что Ruby не может предсказать, каким может быть следующий элемент (вы можете определить бесконечные перечисления, см. премьер - для примера). Поэтому он должен вызывать функцию для получения каждого последующего элемента (обычно это будет each метод).

массивы являются наиболее распространенной коллекцией, поэтому разумно оптимизировать их производительность. Поскольку Ruby много знает о том, как работают массивы, ему не нужно вызывать each но только используйте простой манипуляции указатель что значительно быстрее.

подобные оптимизации существуют для ряда методов массива, таких как zip или count.


Я сказал они одинаковы.

на самом деле они документированы в том же месте под ruby-doc.org:

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

  • Варь.соберите {/item / block } → new_ary
  • Варь.карта {/item / block } → new_ary
  • Варь.соберите → an_enumerator
  • Варь.карта → an_enumerator

вызывает блок один раз для каждого элемента самостоятельно. Создает новый массив, содержащий значения, возвращаемые блоком. См. также Enumerable#collect.
Если блок не задан, вместо него возвращается перечислитель.

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

Я сделал тест, чтобы попытаться ответить на этот вопрос, а затем нашел этот пост, поэтому вот мои выводы (которые немного отличаются от других ответов)

вот контрольный код:

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) 

возможно, псевдоним не бесплатно?


Ruby псевдонимы метода Array#map to 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


на collect и collect! методы являются псевдонимами для map и map!, поэтому их можно использовать взаимозаменяемо. Вот простой способ подтвердить это:

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