Saya memiliki metode di dalam metode. Metode interior bergantung pada loop variabel yang sedang dijalankan. Apakah itu ide yang buruk?
Saya memiliki metode di dalam metode. Metode interior bergantung pada loop variabel yang sedang dijalankan. Apakah itu ide yang buruk?
Jawaban:
PEMBARUAN: Karena jawaban ini sepertinya mendapat perhatian akhir-akhir ini, saya ingin menunjukkan bahwa ada diskusi tentang pelacak masalah Ruby untuk menghapus fitur yang dibahas di sini, yaitu melarang memiliki definisi metode di dalam tubuh metode .
Tidak, Ruby tidak memiliki metode bertingkat.
Anda bisa melakukan sesuatu seperti ini:
class Test1
def meth1
def meth2
puts "Yay"
end
meth2
end
end
Test1.new.meth1
Tapi itu bukan metode bersarang. Saya ulangi: Ruby tidak memiliki metode bersarang.
Apa ini, adalah definisi metode dinamis. Saat Anda berlari meth1
, tubuh meth1
akan dieksekusi. Tubuh kebetulan mendefinisikan metode bernama meth2
, itulah sebabnya setelah dijalankan meth1
sekali, Anda bisa memanggil meth2
.
Tapi dimana meth2
didefinisikan? Yah, ini jelas tidak didefinisikan sebagai metode bersarang, karena tidak ada metode bersarang di Ruby. Ini didefinisikan sebagai metode contoh Test1
:
Test1.new.meth2
# Yay
Selain itu, jelas akan ditentukan ulang setiap kali Anda menjalankan 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
Singkatnya: tidak, Ruby tidak mendukung metode bersarang.
Perhatikan juga bahwa di Ruby, badan metode tidak bisa menjadi closure, hanya badan blok yang bisa. Ini cukup banyak menghilangkan penggunaan kasus besar untuk metode bersarang, karena bahkan jika Ruby didukung metode bersarang, Anda tidak bisa menggunakan variabel metode luar dalam metode bersarang.
PEMBARUAN LANJUTAN: pada tahap selanjutnya , maka, sintaks ini mungkin digunakan kembali untuk menambahkan metode bersarang ke Ruby, yang akan berperilaku seperti yang saya jelaskan: mereka akan dicakup ke metode yang memuatnya, yaitu tidak terlihat dan tidak dapat diakses di luar metode penampungnya tubuh. Dan mungkin, mereka akan memiliki akses ke lingkup leksikal metode yang mereka isi. Namun, jika Anda membaca diskusi yang saya tautkan di atas, Anda dapat mengamati bahwa matz sangat menentang metode bersarang (tetapi masih untuk menghapus definisi metode bersarang).
Sebenarnya itu mungkin. Anda dapat menggunakan procs / lambda untuk ini.
def test(value)
inner = ->() {
value * value
}
inner.call()
end
Tidak, tidak, Ruby memang memiliki metode bertingkat. Periksa ini:
def outer_method(arg)
outer_variable = "y"
inner_method = lambda {
puts arg
puts outer_variable
}
inner_method[]
end
outer_method "x" # prints "x", "y"
Anda bisa melakukan sesuatu seperti ini
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
Ini berguna ketika Anda melakukan hal-hal seperti menulis DSL yang membutuhkan berbagi ruang lingkup antar metode. Tetapi sebaliknya, Anda jauh lebih baik melakukan hal lain, karena seperti jawaban yang lain, inner
didefinisikan ulang setiap kali outer
dipanggil. Jika Anda menginginkan perilaku ini, dan terkadang Anda mungkin menginginkannya, ini adalah cara yang baik untuk mendapatkannya.
Cara Ruby adalah memalsukannya dengan peretasan yang membingungkan yang akan membuat beberapa pengguna bertanya-tanya "Bagaimana sih cara ini bisa bekerja?", Sementara yang kurang penasaran hanya akan menghafal sintaks yang diperlukan untuk menggunakan benda itu. Jika Anda pernah menggunakan Rake atau Rails, Anda pasti pernah melihat hal semacam ini.
Ini peretasan seperti itu:
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
Apa yang dilakukannya adalah menentukan metode tingkat atas yang membuat kelas dan meneruskannya ke blok. Kelas menggunakan method_missing
untuk berpura-pura memiliki metode dengan nama yang Anda pilih. Ini "mengimplementasikan" metode dengan memanggil lambda yang harus Anda sediakan. Dengan menamai objek dengan nama satu huruf, Anda dapat meminimalkan jumlah pengetikan tambahan yang diperlukannya (yang sama dengan yang dilakukan Rails di dalamnya schema.rb
). mlet
dinamai menurut bentuk Common Lisp flet
, kecuali f
singkatan dari "function",m
singkatan dari "method".
Anda menggunakannya seperti ini:
def outer
mlet :inner, ->(x) { x*2 } do |c|
c.inner 12
end
end
Dimungkinkan untuk membuat alat serupa yang memungkinkan beberapa fungsi dalam untuk didefinisikan tanpa tambahan bersarang, tetapi itu membutuhkan peretasan yang lebih buruk dari jenis yang mungkin Anda temukan dalam implementasi Rake atau Rspec. Mencari tahu bagaimana Rspec let!
bekerja akan membawa Anda jauh untuk bisa menciptakan kekejian yang mengerikan.
:-D
Ruby memiliki metode bertingkat, hanya saja metode tersebut tidak melakukan apa yang Anda harapkan
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"