Разница между картой и собиранием в Ruby?

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

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

Ответ 1

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


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

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

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


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

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

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

Ответ 2

Мне сказали, что они одинаковы.

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

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

  • ary.collect {| item | block} → new_ary
  • ary.map {| item | block} → new_ary
  • ary.collect → an_enumerator
  • ary.map → an_enumerator

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

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

Ответ 3

Ruby aliases метод Массив # map для массива # 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

Ответ 4

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

Вот эталонный код:

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) 

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