ฉันพบรหัสนี้ใน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_proc
method บน 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' เพื่อให้obj
1 และ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
กับมันจะตรงข้ามเมื่อนำไปใช้กับProc
Proc
ดังนั้น&: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