Разница между картой и коллекцией в Ruby? - PullRequest
408 голосов
/ 10 марта 2011

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

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

Ответы [ 5 ]

456 голосов
/ 10 марта 2011

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


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

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

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


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

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

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

48 голосов
/ 10 марта 2011

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

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

.

http://www.ruby -doc.org / ядро ​​/ классов / Array.html # M000249

  • ary.collect {| item | блок} → new_ary
  • ary.map {| item | блок} → new_ary
  • ary.collect → an_enumerator
  • ary.map → an_enumerator

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

a = [ "a", "b", "c", "d" ]
a.collect {|x| x + "!" }   #=> ["a!", "b!", "c!", "d!"]
a                          #=> ["a", "b", "c", "d"]
11 голосов
/ 01 января 2016

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

Вот код теста:

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) 

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

7 голосов
/ 27 февраля 2015

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 / ядро-2.2.0 / Array.html # метод-я-карта

4 голосов
/ 01 октября 2018

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

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