web-dev-qa-db-fra.com

Différence entre carte et collection en rubis?

J'ai recherché cela dans Google et obtenu des opinions contradictoires/contradictoires. Existe-t-il une différence entre faire un map et faire un collect sur un tableau dans Ruby/Rails?

Les docs ne semblent pas en suggérer, mais existe-t-il peut-être des différences de méthode ou de performance?

410
sscirrus

Il n'y a pas de différence, en fait, map est implémenté dans C comme rb_ary_collect et enum_collect (par exemple, il y a une différence entre map sur un tableau et sur toute autre énumération, mais aucune différence entre map et collect).


Pourquoi map et collect existent-ils en Ruby? La fonction map comporte de nombreuses conventions de dénomination dans différentes langues. . Wikipedia en donne un aperç :

La fonction map a pour origine des langages de programmation fonctionnels, mais est aujourd'hui prise en charge (ou peut être définie) dans de nombreux langages procéduraux, orientés objet et à paradigmes multiples: dans la bibliothèque de modèles standard de C++, elle s'appelle transform, en C #. Bibliothèque LINQ (3.0), elle est fournie sous forme de méthode d’extension appelée Select. Map est également une opération fréquemment utilisée dans les langages de haut niveau tels que Perl, Python et Ruby; l'opération s'appelle map dans les trois langues. Un alias collect pour map est également fourni dans Ruby (à partir de Smalltalk) [l'emphase mienne]. Common LISP fournit une famille de fonctions semblables à des cartes; celui qui correspond au comportement décrit ici s'appelle mapcar (-car indiquant l'accès à l'aide de l'opération CAR).

Ruby fournit un alias aux programmeurs du monde Smalltalk pour qu'ils se sentent plus à l'aise.


Pourquoi existe-t-il une implémentation différente pour les tableaux et les énumérations? Une énumération est une structure d'itération généralisée, ce qui signifie qu'il n'y a aucun moyen dans lequel Ruby peut prédire ce que le prochain élément peut être (vous pouvez définir un enum infini, voir Prime pour un exemple). Par conséquent, il doit appeler une fonction pour obtenir chaque élément successif (ce sera généralement la méthode each).

Les tableaux étant la collection la plus courante, il est raisonnable d'optimiser leurs performances. Puisque Ruby en sait beaucoup sur le fonctionnement des tableaux, il n'est pas nécessaire d'appeler each, mais vous ne pouvez utiliser que la simple manipulation du pointeur , ce qui est nettement plus rapide.

Des optimisations similaires existent pour un certain nombre de méthodes Array telles que Zip ou count.

459
Jakub Hampl

On m'a dit ce sont les mêmes.

En fait, ils sont documentés au même endroit sous 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 → un_numérateur
  • ary.map → un_numérateur

Invoque le bloc une fois pour chaque élément de soi. Crée un nouveau tableau contenant les valeurs renvoyées par le bloc. Voir aussi Enumerable # collect.
Si aucun bloc n'est donné, un énumérateur est retourné à la place.

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

J'ai fait un test de référence pour essayer de répondre à cette question, puis j'ai trouvé ce billet alors voici mes conclusions (qui diffèrent légèrement des autres réponses)

Voici le code de référence:

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

Et les résultats que j'ai obtenus ont été:

                   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) 

Peut-être qu'un alias n'est pas gratuit?

12
ktec

Ruby appelle la méthode Array # map to Array # collect; ils peuvent être utilisés de façon interchangeable. (Ruby Monk)

En d'autres termes, même code source:

               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

Les méthodes collect et collect! sont des alias pour map et map!, de sorte qu'elles peuvent être utilisées de manière interchangeable. Voici un moyen simple de confirmer que:

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