Perbedaan antara peta dan kumpulkan di Ruby?

428

Saya telah mencarinya di Google dan mendapatkan pendapat yang tidak jelas / kontradiktif - apakah sebenarnya ada perbedaan antara melakukan mapdan mengerjakan collectarray pada Ruby / Rails?

The docs tampaknya tidak menyarankan, tetapi ada mungkin perbedaan dalam metode atau kinerja?

sscirrus
sumber
5
maplebih disukai di Code Golf .
Cary Swoveland
1
Sebagai penjelasan mengapa maplebih disukai di CodeGolf, yang mungkin tidak jelas untuk semua: itu hanya karena collectempat karakter lebih panjang dari map, tetapi fungsionalitas yang sama.
Jochem Schulenklopper
2
Hanya untuk berperan sebagai advokat iblis, saya pribadi menemukan collectlebih mudah dibaca dan alami - ide 'mengumpulkan' catatan dan melakukan X untuk mereka lebih masuk akal bagi saya daripada 'memetakan' catatan dan melakukan X untuk mereka.
sscirrus

Jawaban:

480

Tidak ada perbedaan, sebenarnya mapdiimplementasikan dalam C as rb_ary_collectdan enum_collect(mis. Ada perbedaan antara mappada array dan pada enum lainnya, tetapi tidak ada perbedaan antara mapdan collect).


Mengapa keduanya mapdan collectada di Ruby? The mapfungsi memiliki banyak konvensi penamaan dalam bahasa yang berbeda. Wikipedia memberikan gambaran :

Fungsi peta berasal dari bahasa pemrograman fungsional tetapi saat ini didukung (atau dapat didefinisikan) dalam banyak bahasa prosedural, berorientasi objek, dan multi-paradigma: Dalam Pustaka Template Standar C ++, disebut transform, dalam C # (3.0) Pustaka LINQ, disediakan sebagai metode ekstensi yang disebut Select. Peta juga merupakan operasi yang sering digunakan dalam bahasa tingkat tinggi seperti Perl, Python dan Ruby; operasi ini disebut mapdalam ketiga bahasa ini. Sebuah collectalias untuk peta juga disediakan di Ruby (dari Smalltalk) [penekanan saya]. Common Lisp menyediakan sekumpulan fungsi mirip peta; yang sesuai dengan perilaku yang dijelaskan di sini disebut mapcar(-car menunjukkan akses menggunakan operasi CAR).

Ruby menyediakan alias untuk programmer dari dunia Smalltalk untuk merasa lebih di rumah.


Mengapa ada implementasi yang berbeda untuk array dan enum? Enum adalah struktur iterasi yang digeneralisasi, yang berarti bahwa tidak ada cara di mana Ruby dapat memprediksi apa elemen berikutnya (Anda dapat mendefinisikan enum tak terbatas, lihat Prime untuk contoh). Karena itu ia harus memanggil fungsi untuk mendapatkan setiap elemen berurutan (biasanya ini akan menjadieach metode).

Array adalah koleksi paling umum sehingga masuk akal untuk mengoptimalkan kinerjanya. Karena Ruby tahu banyak tentang cara kerja array, ia tidak perlu memanggil eachtetapi hanya dapat menggunakan manipulasi pointer sederhana yang jauh lebih cepat.

Optimalisasi serupa ada untuk sejumlah metode Array seperti zipatau count.

Jakub Hampl
sumber
14
@Mark Reed tapi kemudian, programmer yang tidak datang dari SmallTalk akan kebingungan dengan memiliki dua fungsi berbeda yang ternyata hanya alias. Ini menyebabkan pertanyaan seperti OP di atas.
SasQ
11
@SasQ Saya tidak setuju - saya pikir akan lebih baik secara keseluruhan jika hanya ada satu nama. Tetapi ada banyak alias lain di Ruby, dan satu fitur dari alias tersebut adalah bahwa ada paralel penamaan yang bagus di antara operasi mengumpulkan , mendeteksi , menyuntikkan , menolak , dan memilih (atau dikenal sebagai peta , menemukan , mengurangi , menolak (tidak ada alias ), dan find_all ).
Mark Reed
5
Memang. Rupanya, Ruby menggunakan alias / sinonim di lebih banyak kesempatan. Sebagai contoh, jumlah elemen dalam array dapat diambil dengan count, lengthatau size. Kata yang berbeda untuk atribut array yang sama, tetapi dengan ini, Ruby memungkinkan Anda untuk memilih kata yang paling tepat untuk kode Anda: apakah Anda ingin jumlah item yang Anda kumpulkan, panjang array, atau ukuran saat ini dari struktur. Pada dasarnya, semuanya sama, tetapi memilih kata yang tepat mungkin membuat kode Anda lebih mudah dibaca, yang merupakan properti bahasa yang bagus.
Jochem Schulenklopper
52

Saya sudah diberitahu mereka sama.

Sebenarnya mereka didokumentasikan di tempat yang sama di ruby-doc.org:

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

  • ary.collect {| item | blokir → → new_ary
  • ary.map {| item | blokir → → new_ary
  • ary.collect → an_enumerator
  • ary.map → an_enumerator

Meminta blok satu kali untuk setiap elemen diri. Membuat array baru yang berisi nilai yang dikembalikan oleh blok. Lihat juga Enumerable # collect.
Jika tidak ada blok yang diberikan, enumerator malah dikembalikan.

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

Saya melakukan tes benchmark untuk mencoba dan menjawab pertanyaan ini, kemudian menemukan posting ini jadi inilah temuan saya (yang sedikit berbeda dari jawaban lainnya)

Berikut ini adalah kode benchmark:

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

Dan hasil yang saya dapatkan adalah:

                   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) 

Mungkin alias tidak gratis?

ktec
sumber
1
Saya tidak yakin apakah perbedaan ini signifikan. Pada tayangan ulang saya mendapatkan hasil yang berbeda dalam kecepatan (bahkan ketika pengumpulan hash Anda tampak lebih lambat, koleksi array Anda tampaknya lebih cepat)
murb
11

Metode collectdan collect!adalah alias untuk mapdan map!, sehingga mereka dapat digunakan secara bergantian. Berikut ini cara mudah untuk mengonfirmasi bahwa:

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

Ruby alias metode Array # map ke Array # collect; mereka dapat digunakan secara bergantian. (Ruby Monk)

Dengan kata lain, kode sumber yang sama:

               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

jeton
sumber
4
Saya berharap dokumentasi dinyatakan secara eksplisit bahwa mereka adalah alias. Saat ini mereka hanya saling referensi, dan keduanya memiliki deskripsi yang sedikit berbeda.
Chris Bloom