ฉันพบรหัสนี้ในRailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
อะไร(&:name)ในmap(&:name)ค่าเฉลี่ย?
ฉันพบรหัสนี้ในRailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
อะไร(&:name)ในmap(&:name)ค่าเฉลี่ย?
คำตอบ:
มันจดชวเลข tags.map(&:name.to_proc).join(' ')
ถ้าfooเป็นวัตถุที่มีto_procเมธอดคุณสามารถส่งผ่านไปยังเมธอด&fooซึ่งจะเรียกfoo.to_procและใช้มันเป็นบล็อกของเมธอด
Symbol#to_procวิธีถูกเพิ่มเข้ามาโดย ActiveSupport แต่ได้รับการรวมอยู่ในทับทิม 1.8.7 นี่คือการใช้งาน:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
&เช่นtags.map(&:name.to_proc).join(' ')
การจดชวเลขเย็นอื่นที่ไม่เป็นที่รู้จักสำหรับคนจำนวนมากคือ
array.each(&method(:foo))
ซึ่งเป็นการจดชวเลข
array.each { |element| foo(element) }
โดยการเรียกmethod(:foo)เราเอาMethodวัตถุจากselfที่แสดงถึงของfooวิธีการและใช้&เพื่อบ่งบอกว่ามันมีto_proc วิธีการProcที่แปลงเป็น
นี้จะเป็นประโยชน์มากเมื่อคุณต้องการที่จะทำสิ่งที่จุดฟรีสไตล์ ตัวอย่างคือการตรวจสอบว่ามีสตริงใด ๆ "foo"ในอาร์เรย์ที่เท่ากับสตริง มีวิธีธรรมดา:
["bar", "baz", "foo"].any? { |str| str == "foo" }
และมีวิธีจุดฟรี:
["bar", "baz", "foo"].any?(&"foo".method(:==))
วิธีที่ต้องการควรเป็นวิธีที่อ่านง่ายที่สุด
array.each{|e| foo(e)}สั้นลงยัง :-) +1 ทุกอย่าง
&methodหรือไม่?
[1,2,3].map(&Array.method(:new))
มันเทียบเท่ากับ
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
ในขณะที่ให้เราทราบว่า#to_procเวทมนตร์เครื่องหมายและสามารถทำงานกับชั้นเรียนใด ๆ ไม่ได้เป็นเพียงสัญลักษณ์ Rubyists หลายคนเลือกที่จะกำหนด#to_procในคลาส Array:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
เครื่องหมายแอมเปอร์แซนด์&ทำงานโดยการส่งto_procข้อความบนตัวถูกดำเนินการซึ่งในโค้ดข้างต้นเป็นของคลาสอาเรย์ และตั้งแต่ฉันกำหนด#to_procmethod บน Array เส้นจะกลายเป็น:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
มันจดชวเลข tags.map { |tag| tag.name }.join(' ')
&ผู้ประกอบการเอกเรียกร้องto_procให้ถูกดำเนินการ ดังนั้นมันจึงไม่เฉพาะเจาะจงกับวิธีการทำแผนที่และอันที่จริงแล้วใช้ได้กับวิธีการใด ๆ ที่นำบล็อกและส่งผ่านอาร์กิวเมนต์หนึ่งรายการขึ้นไปไปยังบล็อก
คำตอบของ Josh Lee นั้นเกือบจะถูกต้องยกเว้นว่ารหัส Ruby ที่เทียบเท่าควรมีดังต่อไปนี้
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
ไม่
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
ด้วยรหัสนี้เมื่อprint [[1,'a'],[2,'b'],[3,'c']].map(&:first)ดำเนินการ Ruby จะแยกอินพุตแรก[1,'a']เป็น 1 และ 'a' เพื่อให้obj1 และargs*'a' เพื่อทำให้เกิดข้อผิดพลาดเนื่องจากวัตถุ Fixnum 1 ไม่ได้มีวิธีการด้วยตนเอง (ซึ่งก็คือ: แรก)
เมื่อ[[1,'a'],[2,'b'],[3,'c']].map(&:first)ถูกประหารชีวิต
:firstเป็นวัตถุสัญลักษณ์ดังนั้นเมื่อ&:firstมอบให้กับวิธีการแผนที่เป็นพารามิเตอร์ Symbol # to_proc ถูกเรียก
แผนที่ส่งข้อความโทรไปที่: first.to_proc พร้อมพารามิเตอร์[1,'a']เช่น:first.to_proc.call([1,'a'])ถูกเรียกใช้งาน
โพรซีเดอร์ to_proc ในคลาส Symbol ส่งข้อความส่งไปยังวัตถุอาร์เรย์ ( [1,'a']) พร้อมพารามิเตอร์ (: ลำดับแรก) เช่น[1,'a'].send(:first)ถูกเรียกใช้งาน
วนซ้ำองค์ประกอบที่เหลือใน[[1,'a'],[2,'b'],[3,'c']]วัตถุ
นี่เหมือนกับการเรียกใช้งาน[[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)นิพจน์
[1,2,3,4,5,6].inject(&:+)คาดหวังว่า - ฉีดคาดหวังแลมบ์ดาที่มีสองพารามิเตอร์ (บันทึกและรายการ) และ:+.to_procส่งมอบ - Proc.new |obj, *args| { obj.send(self, *args) }หรือ{ |m, o| m.+(o) }
มีสองสิ่งเกิดขึ้นที่นี่และสิ่งสำคัญคือต้องเข้าใจทั้งสองอย่าง
ตามที่อธิบายไว้ในคำตอบอื่น ๆSymbol#to_procวิธีการที่ถูกเรียก
แต่เหตุผลที่to_procถูกเรียกบนสัญลักษณ์ก็เพราะมันถูกส่งไปmapเป็นอาร์กิวเมนต์บล็อก การวาง&หน้าอาร์กิวเมนต์ในการเรียกเมธอดทำให้การส่งผ่านวิธีนี้ นี่เป็นความจริงสำหรับวิธีการทับทิมใด ๆ ไม่ใช่แค่mapกับสัญลักษณ์
def some_method(*args, &block)
puts "args: #{args.inspect}"
puts "block: #{block.inspect}"
end
some_method(:whatever)
# args: [:whatever]
# block: nil
some_method(&:whatever)
# args: []
# block: #<Proc:0x007fd23d010da8>
some_method(&"whatever")
# TypeError: wrong argument type String (expected Proc)
# (String doesn't respond to #to_proc)
Symbolได้รับการดัดแปลงเป็นProcเพราะมันผ่านในขณะที่บล็อก เราสามารถแสดงสิ่งนี้ได้โดยพยายามส่ง proc ไปที่.mapโดยไม่มีเครื่องหมายและ:
arr = %w(apple banana)
reverse_upcase = proc { |i| i.reverse.upcase }
reverse_upcase.is_a?(Proc)
=> true
arr.map(reverse_upcase)
# ArgumentError: wrong number of arguments (1 for 0)
# (map expects 0 positional arguments and one block argument)
arr.map(&reverse_upcase)
=> ["ELPPA", "ANANAB"]
แม้ว่ามันจะไม่จำเป็นต้องถูกแปลงวิธีการก็ยังไม่รู้ว่าจะใช้มันอย่างไรเพราะมันคาดว่าจะเป็นการโต้แย้งแบบบล็อก ผ่านมันด้วย&จะช่วยให้.mapบล็อกมันคาดว่า
(&: ชื่อ) ย่อมาจาก (&: name.to_proc) ซึ่งเหมือนกับ tags.map{ |t| t.name }.join(' ')
to_proc มีการใช้งานจริงใน C
map (&: name)รับอ็อบเจกต์นับจำนวน (แท็กในกรณีของคุณ) และรันเมธอดชื่อสำหรับแต่ละอิลิเมนต์ / แท็กส่งออกแต่ละค่าที่ส่งคืนจากเมธอด
มันเป็นชวเลขสำหรับ
array.map { |element| element.name }
ซึ่งส่งคืนอาร์เรย์ของชื่อองค์ประกอบ (แท็ก)
มันเป็นพื้นเรียกวิธีการtag.nameในแต่ละแท็กในอาร์เรย์
มันเป็นชวเลขทับทิมที่เรียบง่าย
แม้ว่าเราจะมีคำตอบที่ดีอยู่แล้ว แต่หากมองผ่านมุมมองของผู้เริ่มต้นฉันต้องการเพิ่มข้อมูลเพิ่มเติม:
map (&: name) มีความหมายอย่างไรใน Ruby
ซึ่งหมายความว่าคุณกำลังผ่านวิธีอื่นเป็นพารามิเตอร์ไปยังฟังก์ชันแผนที่ (ในความเป็นจริงคุณกำลังส่งสัญลักษณ์ที่แปลงเป็น proc แต่นี่ไม่สำคัญในกรณีนี้)
สิ่งที่สำคัญคือคุณมีmethodชื่อnameที่จะใช้โดยวิธีการแผนที่เป็นอาร์กิวเมนต์แทนblockรูปแบบดั้งเดิม
ก่อน&:nameเป็นทางลัดสำหรับ&:name.to_procที่:name.to_procส่งกลับProc(สิ่งที่คล้ายกัน แต่ไม่เหมือนแลมบ์ดา) ว่าเมื่อเรียกว่ามีวัตถุเป็นอาร์กิวเมนต์ (แรก) เรียกnameวิธีการในวัตถุนั้น
ประการที่สองในขณะที่&ในdef foo(&block) ... endแปลงกระชากผ่านไปfooกับมันจะตรงข้ามเมื่อนำไปใช้กับProcProc
ดังนั้น&:name.to_procเป็นบล็อกที่ใช้วัตถุเป็นอาร์กิวเมนต์และเรียกร้องเป็นวิธีการที่มันคือname{ |o| o.name }
นี่:nameคือสัญลักษณ์ที่ชี้ไปที่วิธีการnameของวัตถุแท็ก เมื่อเราผ่าน&:nameไปmapก็จะถือว่าnameเป็นวัตถุ proc สำหรับสั้นtags.map(&:name)ทำหน้าที่เป็น:
tags.map do |tag|
tag.name
end
มันหมายถึง
array.each(&:to_sym.to_proc)
มันเหมือนกับด้านล่าง:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end