Diferença entre mapear e coletar em Ruby?

Eu pesquisei isso e recebi opiniões irregulares / contraditórias – existe alguma diferença entre fazer um map e fazer uma collect em uma matriz em Ruby / Rails?

Os documentos não parecem sugerir nenhum, mas há diferenças no método ou no desempenho?

Não há diferença, na verdade o map é implementado em C como rb_ary_collect e enum_collect (por exemplo, há uma diferença entre o map em uma matriz e em qualquer outro enum, mas não há diferença entre map e collect ).


Por que tanto map quanto collect existem em Ruby? A function map tem muitas convenções de nomenclatura em diferentes idiomas. A Wikipedia fornece uma visão geral :

A function map originou-se em linguagens de programação funcionais, mas hoje é suportada (ou pode ser definida) em muitas linguagens procedurais, orientadas a objects e multi-paradigmáticas: Na Standard Template Library de C ++, é chamada de transform , em C # (3.0) ‘ s biblioteca LINQ, é fornecida como um método de extensão chamado Select . O mapa também é uma operação freqüentemente usada em linguagens de alto nível, como Perl, Python e Ruby; a operação é chamada de map em todos esses três idiomas. Um alias de collect para o mapa também é fornecido em Ruby (do Smalltalk) [ênfase minha]. Common Lisp fornece uma família de funções semelhantes a mapas; o correspondente ao comportamento descrito aqui é chamado de mapcar ( mapcar indicando access usando a operação CAR).

Ruby fornece um alias para programadores do mundo Smalltalk para se sentirem mais em casa.


Por que há uma implementação diferente para matrizes e enums? Um enum é uma estrutura de iteração generalizada, o que significa que não há nenhuma maneira em que Ruby possa prever o que o próximo elemento pode ser (você pode definir enums infinitos, veja Prime para um exemplo). Portanto, ele deve chamar uma function para obter cada elemento sucessivo (normalmente, esse será o método de each ).

Os arrays são a coleção mais comum, portanto, é razoável otimizar seu desempenho. Como Ruby sabe muito sobre como os arrays funcionam, ele não precisa chamar each mas só pode usar a manipulação simples de pointers, que é significativamente mais rápida.

Otimizações semelhantes existem para um número de methods Array como zip ou count .

Já me disseram que eles são iguais.

Na verdade, eles estão documentados no mesmo lugar em ruby-doc.org:

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

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

Invoca o bloco uma vez para cada elemento do self. Cria uma nova matriz contendo os valores retornados pelo bloco. Veja também Enumerable # collect.
Se nenhum bloco for fornecido, um enumerador será retornado.

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

Eu fiz um teste de benchmark para tentar responder a esta pergunta, então encontrei este post então aqui estão minhas descobertas (que diferem ligeiramente das outras respostas)

Aqui está o código de referência:

 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 

E os resultados que recebi foram:

  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) 

Talvez um alias não seja gratuito?

Ruby aliases o método Array # map to Array # collect; eles podem ser usados ​​de forma intercambiável. (Ruby Monk)

Em outras palavras, o mesmo código-fonte:

  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