คุณจะส่งผ่านข้อโต้แย้งไปยัง define_method ได้อย่างไร


155

ฉันต้องการส่งอาร์กิวเมนต์ไปยังวิธีที่กำหนดโดยใช้ define_method ฉันจะทำอย่างไร

คำตอบ:


198

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

define_method(:say_hi) { |other| puts "Hi, " + other }

นั่นเป็นเพียงเรื่องของการเฆี่ยนตีอย่างแท้จริง เก่งมาก Kevin Costner
Darth Egregious

90

... และถ้าคุณต้องการพารามิเตอร์ทางเลือก

 class Bar
   define_method(:foo) do |arg=nil|                  
     arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> nil
 a.foo 1
 # => 1

... โต้แย้งได้มากเท่าที่คุณต้องการ

 class Bar
   define_method(:foo) do |*arg|                  
     arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> []
 a.foo 1
 # => [1]
 a.foo 1, 2 , 'AAA'
 # => [1, 2, 'AAA']

... การรวมกันของ

 class Bar
   define_method(:foo) do |bubla,*arg|
     p bubla                  
     p arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> wrong number of arguments (0 for 1)
 a.foo 1
 # 1
 # []

 a.foo 1, 2 ,3 ,4
 # 1
 # [2,3,4]

... พวกเขาทุกคน

 class Bar
   define_method(:foo) do |variable1, variable2,*arg, &block|  
     p  variable1     
     p  variable2
     p  arg
     p  block.inspect                                                                              
   end   
 end
 a = Bar.new      
 a.foo :one, 'two', :three, 4, 5 do
   'six'
 end

ปรับปรุง

Ruby 2.0 แนะนำเครื่องหมายคู่**(สองดาว) ซึ่ง ( ฉันพูดถึง ) ทำ:

Ruby 2.0 แนะนำอาร์กิวเมนต์คำหลักและ ** ทำหน้าที่เหมือน * แต่สำหรับอาร์กิวเมนต์คำหลัก ส่งคืนแฮชที่มีคู่ของคีย์ / ค่า

... และแน่นอนว่าคุณสามารถใช้มันในวิธีการกำหนดด้วย :)

 class Bar 
   define_method(:foo) do |variable1, variable2,*arg,**options, &block|
     p  variable1
     p  variable2
     p  arg
     p  options
     p  block.inspect
   end 
 end 
 a = Bar.new
 a.foo :one, 'two', :three, 4, 5, ruby: 'is awesome', foo: :bar do
   'six'
 end
# :one
# "two"
# [:three, 4, 5]
# {:ruby=>"is awesome", :foo=>:bar}

ตัวอย่างแอตทริบิวต์ที่มีชื่อ:

 class Bar
   define_method(:foo) do |variable1, color: 'blue', **other_options, &block|
     p  variable1
     p  color
     p  other_options
     p  block.inspect
   end
 end
 a = Bar.new
 a.foo :one, color: 'red', ruby: 'is awesome', foo: :bar do
   'six'
 end
# :one
# "red"
# {:ruby=>"is awesome", :foo=>:bar}

ฉันพยายามสร้างตัวอย่างด้วยอาร์กิวเมนต์คำหลักเครื่องหมายและเครื่องหมายสองครั้งทั้งหมดในที่เดียว:

 define_method(:foo) do |variable1, variable2,*arg, i_will_not: 'work', **options, &block|
    # ...

หรือ

 define_method(:foo) do |variable1, variable2, i_will_not: 'work', *arg, **options, &block|
    # ...

... แต่นี่จะไม่ทำงานดูเหมือนว่าจะมีข้อ จำกัด เมื่อคุณคิดว่ามันสมเหตุสมผลเนื่องจากตัวดำเนินการ splat คือ "การจับอาร์กิวเมนต์ที่เหลือทั้งหมด" และเครื่องหมายสองครั้งคือ "การจับอาร์กิวเมนต์ของคำหลักที่เหลือทั้งหมด" ดังนั้นการผสมคำหลักเหล่านั้นจะทำให้ตรรกะที่คาดไว้ (ฉันไม่มีข้อมูลอ้างอิงเพื่อพิสูจน์จุดนี้เลย!)

อัปเดต 2018 สิงหาคม:

บทความสรุป: https://blog.eq8.eu/til/metaprogramming-ruby-examples.html


ที่น่าสนใจ - พิเศษช่วงที่ 4: มันใช้งานได้กับ 1.8.7! บล็อกแรกไม่ทำงานใน 1.8.7 และบล็อกที่สองมีตัวพิมพ์ผิด (ควรa.foo 1แทนfoo 1) ขอขอบคุณ!
Sony Santos

1
ขอบคุณสำหรับคำติชมพิมพ์ผิดได้รับการแก้ไข ... ในทับทิม 1.9.3 และ 1.9.2 ตัวอย่างทั้งหมดทำงานและฉันบวกที่ 1.9.1 เกินไป (แต่ไม่ได้ลอง)
เทียบเท่า

ฉันรวมคำตอบนี้กับคำตอบที่ได้รับการยอมรับที่stackoverflow.com/questions/4470108/…เพื่อคิดวิธีการเขียนทับ (ไม่ใช่ลบล้าง) วิธีที่รันไทม์ที่ใช้ args และบล็อกที่เป็นตัวเลือกและยังคงสามารถเรียกใช้วิธีดั้งเดิมกับ args และบล็อก อ้าทับทิม โดยเฉพาะฉันต้องการเขียนทับ Savon :: Client.request ใน dev env ของฉันสำหรับการเรียก API เดียวไปยังโฮสต์ที่ฉันสามารถเข้าถึงได้เฉพาะในการผลิต ไชโย!
pduey

59

นอกเหนือจากคำตอบของ Kevin Conner: อาร์กิวเมนต์ของบล็อกไม่สนับสนุนซีแมนทิกส์เหมือนกันกับเมธอดอาร์กิวเมนต์ คุณไม่สามารถกำหนดอาร์กิวเมนต์เริ่มต้นหรือบล็อกอาร์กิวเมนต์

สิ่งนี้ได้รับการแก้ไขใน Ruby 1.9 พร้อมกับไวยากรณ์ "stabby lambda" ทางเลือกใหม่ซึ่งรองรับ semantics วิธีการโต้แย้งแบบเต็ม

ตัวอย่าง:

# Works
def meth(default = :foo, *splat, &block) puts 'Bar'; end

# Doesn't work
define_method :meth { |default = :foo, *splat, &block| puts 'Bar' }

# This works in Ruby 1.9 (modulo typos, I don't actually have it installed)
define_method :meth, ->(default = :foo, *splat, &block) { puts 'Bar' }

3
ที่จริงแล้วฉันเชื่อว่าการบล็อกข้อโต้แย้งใน define_method สนับสนุน splat ซึ่งสามารถจัดให้มีวิธีรอบในการกำหนดอาร์กิวเมนต์เริ่มต้นได้เช่นกัน
Chinasaur

1
Chinasaur นั้นถูกต้องเกี่ยวกับข้อโต้แย้งของบล็อกที่อนุญาตให้ splats ฉันยืนยันเรื่องนี้ทั้งใน Ruby 1.8.7 และ 1.9.1
Peter Wagenet

ขอบคุณฉันลืมเรื่องนี้ แก้ไขแล้ว
Jörg W Mittag

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.