Tôi có một phương thức bên trong một phương thức. Phương pháp nội thất phụ thuộc vào một vòng lặp biến đang được chạy. đó có phải là ý tưởng tồi tệ không?
Câu trả lời:
CẬP NHẬT: Vì câu trả lời này có vẻ đã thu hút được một số sự quan tâm gần đây, tôi muốn chỉ ra rằng có một cuộc thảo luận về trình theo dõi vấn đề Ruby để loại bỏ tính năng được thảo luận ở đây, cụ thể là cấm có các định nghĩa phương thức bên trong thân phương thức .
Không, Ruby không có các phương thức lồng nhau.
Bạn có thể làm điều gì đó như sau:
class Test1
def meth1
def meth2
puts "Yay"
end
meth2
end
end
Test1.new.meth1
Nhưng đó không phải là một phương pháp lồng nhau. Tôi nhắc lại: Ruby không có các phương thức lồng nhau.
Đây là một định nghĩa phương thức động. Khi bạn chạy meth1
, phần thân của meth1
sẽ được thực thi. Phần thân chỉ xảy ra để xác định một phương thức có tên meth2
, đó là lý do tại sao sau khi chạy meth1
một lần, bạn có thể gọi meth2
.
Nhưng meth2
định nghĩa ở đâu? Chà, rõ ràng nó không được định nghĩa là một phương thức lồng nhau, vì không có phương thức lồng nhau nào trong Ruby. Nó được định nghĩa là một phương thức thể hiện của Test1
:
Test1.new.meth2
# Yay
Ngoài ra, nó rõ ràng sẽ được xác định lại mỗi khi bạn chạy 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
Tóm lại: không, Ruby không hỗ trợ các phương thức lồng nhau.
Cũng lưu ý rằng trong Ruby, các thân phương thức không thể là các bao đóng, chỉ các thân khối mới có thể. Điều này giúp loại bỏ khá nhiều trường hợp sử dụng chính cho các phương thức lồng nhau, vì ngay cả khi Ruby hỗ trợ các phương thức lồng nhau, bạn không thể sử dụng các biến của phương thức bên ngoài trong phương thức lồng nhau.
CẬP NHẬT TIẾP THEO: tại một sau sân khấu, sau đó, cú pháp này có thể tái sử dụng cho thêm các phương pháp lồng nhau để Ruby, mà sẽ hành xử theo cách tôi đã mô tả: họ sẽ được scoped phương pháp chứa của họ, tức là bên ngoài vô hình và không thể tiếp cận của phương pháp chứa của họ thân hình. Và có thể, họ sẽ có quyền truy cập vào phạm vi từ vựng của phương thức chứa của họ. Tuy nhiên, nếu bạn đọc phần thảo luận mà tôi đã liên kết ở trên, bạn có thể thấy rằng matz rất chống lại các phương thức lồng nhau (nhưng vẫn để loại bỏ các định nghĩa phương thức lồng nhau).
Trên thực tế, nó có thể. Bạn có thể sử dụng procs / lambda cho việc này.
def test(value)
inner = ->() {
value * value
}
inner.call()
end
Không, không, Ruby có các phương thức lồng nhau. Kiểm tra điều này:
def outer_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x" # prints "x", "y"
Bạn có thể làm điều gì đó như thế nà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
Điều này rất hữu ích khi bạn đang làm những việc như viết DSL yêu cầu chia sẻ phạm vi giữa các phương thức. Nhưng nếu không, bạn nên làm bất cứ điều gì khác, vì như các câu trả lời khác đã nói, inner
được xác định lại bất cứ khi nào outer
được gọi. Nếu bạn muốn hành vi này và đôi khi có thể, đây là một cách tốt để thực hiện.
Cách thức của Ruby là giả mạo nó bằng các bản hack khó hiểu khiến một số người dùng tự hỏi "Làm thế nào mà cái này lại hoạt động được?", Trong khi những người ít tò mò hơn sẽ chỉ đơn giản là ghi nhớ cú pháp cần thiết để sử dụng thứ đó. Nếu bạn đã từng sử dụng Rake hoặc Rails, bạn sẽ thấy loại này.
Đây là một vụ hack:
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
Điều đó làm là xác định một phương thức cấp cao nhất tạo ra một lớp và chuyển nó đến một khối. Lớp sử dụng method_missing
để giả vờ rằng nó có một phương thức với tên bạn đã chọn. Nó "triển khai" phương thức bằng cách gọi lambda mà bạn phải cung cấp. Bằng cách đặt tên cho đối tượng bằng tên một chữ cái, bạn có thể giảm thiểu số lượng nhập thêm mà nó yêu cầu (giống như điều mà Rails thực hiện với nó schema.rb
). mlet
được đặt tên theo dạng Common Lisp flet
, ngoại trừ trường hợp f
là viết tắt của "function", m
là viết tắt của "method".
Bạn sử dụng nó như thế này:
def outer
mlet :inner, ->(x) { x*2 } do |c|
c.inner 12
end
end
Có thể tạo ra một cấu trúc tương tự cho phép xác định nhiều hàm bên trong mà không cần lồng bổ sung, nhưng điều đó đòi hỏi một kiểu hack thậm chí còn xấu hơn mà bạn có thể tìm thấy trong quá trình triển khai của Rake hoặc Rspec. Tìm hiểu cách thức let!
hoạt động của Rspec sẽ giúp bạn có một chặng đường dài để có thể tạo ra một thứ ghê tởm khủng khiếp như vậy.
:-D
Ruby có các phương thức lồng nhau, chỉ có điều chúng không làm những gì bạn mong đợi
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"