ความแตกต่างระหว่างแผนที่และสะสมในทับทิม?


427

ฉันได้ Googled สิ่งนี้และมีความคิดเห็นที่เป็นหย่อม ๆ / ขัดแย้ง - จริง ๆ แล้วมีความแตกต่างระหว่างการทำ a mapและ a collectarray ใน Ruby / Rails หรือไม่?

เอกสารดูเหมือนจะไม่แนะนำใด ๆ แต่อาจจะมีความแตกต่างในวิธีการหรือประสิทธิภาพ?


5
mapเป็นที่ต้องการในรหัสกอล์ฟ
Cary Swoveland

1
ตามคำอธิบายว่าทำไมจึงmapเป็นที่ต้องการของ CodeGolf ซึ่งอาจไม่ชัดเจนสำหรับทุกคน: เป็นเพราะcollectตัวละครยาวกว่าตัวละครสี่ตัวmapแต่ในการใช้งานเหมือนกัน
Jochem Schulenklopper

2
เพียงเพื่อให้การสนับสนุนของปีศาจฉันพบว่าการcollectอ่านและการเก็บบันทึกเป็นธรรมชาติมากขึ้นโดยส่วนตัวแล้วความคิดในการ 'รวบรวม' การบันทึกและการทำ X ให้พวกเขาทำให้ฉันรู้สึกเป็นธรรมชาติมากขึ้นกว่าการบันทึกการทำแผนที่
sscirrus

คำตอบ:


479

ไม่มีความแตกต่างในความmapเป็นจริงถูกนำมาใช้ใน C เป็นrb_ary_collectและenum_collect(เช่นมีความแตกต่างระหว่างmapในอาร์เรย์และใน Enum อื่น ๆ แต่ไม่มีความแตกต่างระหว่างmapและcollect)


ทำไมทั้งสองmapและcollectมีอยู่ในทับทิม? mapฟังก์ชั่นนี้มีข้อกำหนดการตั้งชื่อจำนวนมากในภาษาต่างๆ Wikipedia ให้ภาพรวม :

ฟังก์ชั่นแผนที่มีต้นกำเนิดในภาษาโปรแกรมที่ใช้งานได้ แต่ได้รับการสนับสนุนในวันนี้ (หรืออาจกำหนด) ในภาษาเชิงกระบวนการเชิงวัตถุและหลายกระบวนทัศน์เช่นกัน: ในไลบรารีแม่แบบมาตรฐานของ C ++ จะถูกเรียกtransformในภาษา C # (3.0) ห้องสมุด LINQ Selectมันให้เป็นวิธีขยายที่เรียกว่า แผนที่ยังใช้งานบ่อยในภาษาระดับสูงเช่น Perl, Python และ Ruby; การดำเนินการถูกเรียกmapในภาษาทั้งสามนี้ นามแฝงสำหรับแผนที่นอกจากนี้ยังมีในทับทิม (จากสมอลล์ทอล์ค) [ผมขอย้ำ] Common Lisp นำเสนอฟังก์ชั่นคล้ายแผนที่ สิ่งที่สอดคล้องกับพฤติกรรมที่อธิบายไว้ที่นี่จะถูกเรียก(- ระบุว่ารถเข้าถึงโดยใช้การทำงานของ CAR)collectmapcar

Ruby นำเสนอนามแฝงสำหรับโปรแกรมเมอร์จากโลก Smalltalk เพื่อให้รู้สึกเหมือนอยู่บ้านมากขึ้น


ทำไมจึงมีการใช้งานที่แตกต่างกันสำหรับอาร์เรย์และ enums? Enum เป็นโครงสร้างการทำซ้ำทั่วไปซึ่งหมายความว่าไม่มีทางที่ Ruby สามารถทำนายได้ว่าองค์ประกอบถัดไปสามารถเป็นอะไรได้ (คุณสามารถกำหนด enums ที่ไม่มีที่สิ้นสุดดูที่Primeสำหรับตัวอย่าง) ดังนั้นจึงต้องเรียกใช้ฟังก์ชันเพื่อรับองค์ประกอบที่ต่อเนื่องกัน (โดยทั่วไปจะเป็นeachวิธีการ)

อาร์เรย์เป็นคอลเลกชันที่พบบ่อยที่สุดดังนั้นจึงมีเหตุผลที่จะเพิ่มประสิทธิภาพให้เหมาะสม เนื่องจาก Ruby รู้มากเกี่ยวกับวิธีการทำงานของอาเรย์จึงไม่จำเป็นต้องโทรeachแต่สามารถใช้การชี้แบบง่าย ๆซึ่งทำได้เร็วกว่ามาก

การเพิ่มประสิทธิภาพที่คล้ายกันที่มีอยู่สำหรับจำนวนของวิธีการเช่นอาร์เรย์หรือzipcount


13
@ Mark Reed แต่แล้วโปรแกรมเมอร์ที่ไม่ได้มาจาก SmallTalk จะเป็นผู้สับสนโดยมีสองฟังก์ชั่นที่แตกต่างกันซึ่งกลายเป็นเพียงนามแฝง มันทำให้เกิดคำถามเช่น OP หนึ่งข้างต้น
SasQ

10
@SasQ ฉันไม่เห็นด้วย - ฉันคิดว่ามันจะดีกว่าโดยรวมถ้ามีเพียงชื่อเดียว แต่ก็ยังมีความอุดมสมบูรณ์ของนามแฝงอื่น ๆ ในรูบีและเป็นหนึ่งในคุณสมบัติของ aliasing คือว่ามีการตั้งชื่อแบบขนานที่ดีในหมู่การดำเนินงานในการเก็บรวบรวม , ตรวจสอบ , ฉีด , ปฏิเสธและเลือก (หรือที่รู้จักแผนที่ , หา , ลด , ปฏิเสธ (ไม่นามแฝง ) และfind_all )
Mark Reed

4
จริง เห็นได้ชัดว่าทับทิมใช้นามแฝง / คำเหมือนในบางโอกาส ยกตัวอย่างเช่นจำนวนขององค์ประกอบในอาร์เรย์สามารถเรียกดูด้วยcount, หรือlength sizeคำที่แตกต่างกันสำหรับแอตทริบิวต์เดียวกันของอาร์เรย์ แต่ด้วยสิ่งนี้ Ruby ช่วยให้คุณเลือกคำที่เหมาะสมที่สุดสำหรับรหัสของคุณ: คุณต้องการจำนวนรายการที่คุณรวบรวมความยาวของอาร์เรย์หรือขนาดปัจจุบันของ โครงสร้าง. โดยพื้นฐานแล้วมันเหมือนกันทั้งหมด แต่การเลือกคำที่ถูกต้องอาจทำให้โค้ดของคุณอ่านง่ายขึ้นซึ่งเป็นคุณสมบัติที่ดีของภาษา
Jochem Schulenklopper

51

ฉันเคยบอกว่าพวกเขาเหมือนกัน

จริงๆแล้วพวกเขาได้รับการบันทึกไว้ในที่เดียวกันภายใต้ 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

เรียกบล็อกหนึ่งครั้งสำหรับแต่ละองค์ประกอบของตัวเอง สร้างอาร์เรย์ใหม่ที่มีค่าที่ส่งคืนโดยบล็อก ดูเพิ่มเติมที่ # จำนวนที่รวบรวมได้
หากไม่มีการกำหนดบล็อกตัวแจงนับจะถูกส่งคืนแทน

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

2
เพื่อให้ละเอียด: http://www.ruby-doc.org/core/classes/Enumerable.html#method-i-map
Andre Helberg

13

ฉันได้ทำการทดสอบเกณฑ์มาตรฐานเพื่อลองและตอบคำถามนี้แล้วพบโพสต์นี้ดังนั้นนี่คือสิ่งที่ฉันค้นพบ (ซึ่งแตกต่างจากคำตอบอื่น ๆ เล็กน้อย)

นี่คือรหัสมาตรฐาน:

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) 

บางทีนามแฝงนั้นฟรี


1
ฉันไม่แน่ใจว่าความแตกต่างเหล่านี้มีความสำคัญหรือไม่ บนรันใหม่ฉันได้รับผลลัพธ์ที่แตกต่างกันในความเร็ว (แม้ในขณะที่การรวบรวมแฮชของคุณดูเหมือนจะช้าลง แต่การสะสมอาร์เรย์ของคุณก็จะเร็วขึ้น)
44907

10

The collectและcollect!method เป็น alias ถึงmapและmap!ดังนั้นจึงสามารถใช้แทนกันได้ นี่เป็นวิธีที่ง่ายในการยืนยันว่า:

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

7

Ruby ใช้นามแฝงวิธี Array # map ไปที่ Array # collect; พวกเขาสามารถใช้แทนกันได้ (Ruby Monk)

กล่าวอีกนัยหนึ่งรหัสแหล่งเดียวกัน:

               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
ฉันต้องการเอกสารที่ระบุไว้อย่างชัดเจนว่าพวกเขาเป็นนามแฝง ในขณะที่พวกเขาเพียงแค่อ้างอิงซึ่งกันและกันและทั้งสองมีคำอธิบายที่แตกต่างกันเล็กน้อย
Chris Bloom
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.