ฉันมีวิธีการภายในของวิธีการ วิธีการตกแต่งภายในขึ้นอยู่กับลูปตัวแปรที่กำลังรัน เป็นความคิดที่ไม่ดี?
ฉันมีวิธีการภายในของวิธีการ วิธีการตกแต่งภายในขึ้นอยู่กับลูปตัวแปรที่กำลังรัน เป็นความคิดที่ไม่ดี?
คำตอบ:
UPDATE: เนื่องจากคำตอบนี้ดูเหมือนว่าจะได้รับความสนใจบางอย่างเมื่อเร็ว ๆ นี้ผมอยากจะชี้ให้เห็นว่ามีการอภิปรายเกี่ยวกับปัญหาที่ติดตามทับทิมจะเอาคุณลักษณะที่กล่าวถึงที่นี่คือการห้ามมีคำจำกัดความของวิธีการที่อยู่ภายในร่างกายวิธีการ
ไม่ Ruby ไม่มีเมธอดซ้อนกัน
คุณสามารถทำสิ่งนี้:
class Test1
def meth1
def meth2
puts "Yay"
end
meth2
end
end
Test1.new.meth1
แต่นั่นไม่ใช่วิธีการซ้อนกัน ฉันพูดซ้ำ: Ruby ไม่มีวิธีการซ้อนกัน
นี่คืออะไรคือนิยามวิธีการแบบไดนามิก เมื่อคุณวิ่งmeth1
ร่างกายของmeth1
จะถูกดำเนินการ ร่างกายเกิดขึ้นเพื่อกำหนดเมธอดที่ชื่อmeth2
ซึ่งเป็นเหตุผลว่าทำไมหลังจากเรียกใช้meth1
ครั้งเดียวคุณสามารถโทรmeth2
ได้
แต่meth2
กำหนดไว้ที่ไหน? เห็นได้ชัดว่ามันไม่ได้ถูกกำหนดให้เป็นเมธอดซ้อนกันเนื่องจากไม่มีเมธอดซ้อนใน Ruby กำหนดเป็นวิธีการเช่นTest1
:
Test1.new.meth2
# Yay
นอกจากนี้จะมีการกำหนดนิยามใหม่ทุกครั้งที่คุณวิ่งmeth1
:
Test1.new.meth1
# Yay
Test1.new.meth1
# test1.rb:3: warning: method redefined; discarding old meth2
# test1.rb:3: warning: previous definition of meth2 was here
# Yay
กล่าวโดยย่อ: ไม่ Ruby ไม่สนับสนุนวิธีการซ้อนกัน
โปรดทราบว่าใน Ruby ไม่สามารถปิดเมธอดบอดี้ได้ แต่บล็อกบอดี้เท่านั้นที่ทำได้ นี้สวยมากกำจัดกรณีใช้งานที่สำคัญสำหรับวิธีการที่ซ้อนกันตั้งแต่แม้กระทั่งถ้าทับทิมสนับสนุนวิธีการซ้อนกันคุณจะไม่สามารถใช้ตัวแปรวิธีการนอกในวิธีการที่ซ้อนกัน
อัปเดตอย่างต่อเนื่อง: ในระยะต่อมาไวยากรณ์นี้อาจถูกนำมาใช้ใหม่เพื่อเพิ่มเมธอดที่ซ้อนกันให้กับ Ruby ซึ่งจะทำงานตามที่ฉันอธิบายไว้: พวกเขาจะถูกกำหนดขอบเขตไปยังเมธอดที่มีอยู่นั่นคือมองไม่เห็นและไม่สามารถเข้าถึงได้นอกเมธอดที่มีอยู่ ร่างกาย. และอาจเป็นไปได้ว่าพวกเขาสามารถเข้าถึงขอบเขตคำศัพท์ของวิธีการที่มีอยู่ อย่างไรก็ตามหากคุณอ่านการสนทนาที่ฉันเชื่อมโยงไว้ข้างต้นคุณจะสังเกตได้ว่า matz ต่อต้านเมธอดที่ซ้อนกันอย่างมาก (แต่ยังคงใช้สำหรับการลบคำจำกัดความของเมธอดที่ซ้อนกัน)
จริงๆแล้วมันเป็นไปได้ คุณสามารถใช้ procs / lambda สำหรับสิ่งนี้
def test(value)
inner = ->() {
value * value
}
inner.call()
end
ไม่ไม่ Ruby มีวิธีการซ้อนกัน ตรวจสอบสิ่งนี้:
def outer_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x" # prints "x", "y"
คุณสามารถทำสิ่งนี้ได้
module Methods
define_method :outer do
outer_var = 1
define_method :inner do
puts "defining inner"
inner_var = outer_var +1
end
outer_var
end
extend self
end
Methods.outer
#=> defining inner
#=> 1
Methods.inner
#=> 2
สิ่งนี้มีประโยชน์เมื่อคุณทำสิ่งต่างๆเช่นการเขียน DSL ซึ่งต้องการการแบ่งปันขอบเขตระหว่างวิธีการ แต่อย่างอื่นคุณจะดีกว่าในการทำอย่างอื่นเพราะอย่างที่คำตอบอื่น ๆ กล่าวว่าinner
จะถูกกำหนดใหม่ทุกครั้งที่outer
ถูกเรียก หากคุณต้องการพฤติกรรมนี้และบางครั้งคุณก็อาจจะเป็นวิธีที่ดี
วิธี Ruby คือการปลอมแปลงด้วยการแฮ็กที่สับสนซึ่งจะทำให้ผู้ใช้บางคนสงสัยว่า "วิธีนี้ใช้งานได้อย่างไร" ในขณะที่คนที่อยากรู้อยากเห็นน้อยลงจะจดจำไวยากรณ์ที่จำเป็นในการใช้สิ่งนี้ หากคุณเคยใช้ Rake หรือ Rails คุณเคยเห็นสิ่งนี้มาแล้ว
นี่คือการแฮ็ก:
def mlet(name,func)
my_class = (Class.new do
def initialize(name,func)
@name=name
@func=func
end
def method_missing(methname, *args)
puts "method_missing called on #{methname}"
if methname == @name
puts "Calling function #{@func}"
@func.call(*args)
else
raise NoMethodError.new "Undefined method `#{methname}' in mlet"
end
end
end)
yield my_class.new(name,func)
end
สิ่งที่ทำคือกำหนดวิธีการระดับบนสุดที่สร้างคลาสและส่งต่อไปยังบล็อก ชั้นเรียนใช้method_missing
เพื่อหลอกว่ามีวิธีการที่มีชื่อที่คุณเลือก มัน "ดำเนินการ" วิธีการโดยเรียกแลมบ์ดาที่คุณต้องระบุ ด้วยการตั้งชื่อวัตถุด้วยชื่อตัวอักษรเดียวคุณสามารถลดจำนวนการพิมพ์พิเศษที่ต้องการได้ (ซึ่งเป็นสิ่งเดียวกับที่ Rails ทำในschema.rb
วัตถุนั้น) mlet
ได้รับการตั้งชื่อตามรูปแบบ Common Lisp flet
ยกเว้นที่f
ย่อมาจาก "function" m
ย่อมาจาก "method"
คุณใช้สิ่งนี้:
def outer
mlet :inner, ->(x) { x*2 } do |c|
c.inner 12
end
end
เป็นไปได้ที่จะสร้างการคุมกำเนิดที่คล้ายกันซึ่งช่วยให้สามารถกำหนดฟังก์ชันภายในได้หลายฟังก์ชันโดยไม่ต้องซ้อนกันเพิ่มเติม แต่ต้องใช้การแฮ็กประเภทที่น่าเกลียดยิ่งกว่าที่คุณอาจพบในการใช้งานของ Rake หรือ Rspec การหาคำตอบว่าlet!
ผลงานของ Rspec จะช่วยให้คุณสามารถสร้างสิ่งที่น่ารังเกียจที่น่าสยดสยองได้อย่างไร
:-D
Ruby มีวิธีการซ้อนกัน แต่พวกมันไม่ทำในสิ่งที่คุณคาดหวัง
1.9.3p484 :001 > def kme; 'kme'; def foo; 'foo'; end; end
=> nil
1.9.3p484 :003 > self.methods.include? :kme
=> true
1.9.3p484 :004 > self.methods.include? :foo
=> false
1.9.3p484 :005 > kme
=> nil
1.9.3p484 :006 > self.methods.include? :foo
=> true
1.9.3p484 :007 > foo
=> "foo"