Apakah mungkin memiliki Metode di dalam Metode?

89

Saya memiliki metode di dalam metode. Metode interior bergantung pada loop variabel yang sedang dijalankan. Apakah itu ide yang buruk?

Perjalanan
sumber
2
Dapatkah Anda membagikan contoh kode atau setidaknya persamaan logis dari apa yang Anda coba lakukan.
Aaron Scruggs

Jawaban:

165

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 meth1akan dieksekusi. Tubuh kebetulan mendefinisikan metode bernama meth2, itulah sebabnya setelah dijalankan meth1sekali, Anda bisa memanggil meth2.

Tapi dimana meth2didefinisikan? 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).

Jörg W Mittag
sumber
6
Namun, Anda juga dapat menunjukkan cara membuat lambda penutupan dalam metode untuk KERING, atau menjalankan rekursi.
Phrogz
119
Saya merasa Ruby mungkin tidak memiliki metode bersarang.
Mark Thomas
16
@ Mark Thomas: Apakah saya lupa menyebutkan bahwa Ruby tidak memiliki metode bersarang? :-) Serius: pada saat saya menulis jawaban ini, sudah ada tiga jawaban, yang masing-masing menyatakan bahwa Ruby memang memiliki metode bersarang. Beberapa dari jawaban itu bahkan mendapat suara positif meski terang-terangan salah. Bahkan ada yang diterima oleh OP, lagi-lagi meski salah. Potongan kode yang digunakan jawaban untuk membuktikan bahwa Ruby mendukung metode bersarang, sebenarnya membuktikan sebaliknya, tetapi tampaknya baik upvoters maupun OP benar-benar peduli untuk memeriksanya. Jadi, saya memberikan satu jawaban yang benar untuk setiap kesalahan. :-)
Jörg W Mittag
10
Ketika Anda menyadari bahwa ini semua hanyalah instruksi untuk Kernel yang memodifikasi tabel dan bahwa metode dan kelas dan modul semua hanya entri dalam tabel dan tidak benar-benar nyata, Anda menjadi seperti Neo ketika dia melihat seperti apa Matrix itu. Kemudian Anda benar-benar dapat menjadi filosofis dan mengatakan bahwa selain metode bersarang, bahkan tidak ada metode. Bahkan tidak ada agen. Mereka adalah program dalam matriks. Bahkan steak juicy yang Anda makan hanyalah sebuah entri di meja.
mydoghasworms
3
Tidak ada metode, kode Anda hanyalah simulasi di The Matrix
bbozo
13

Sebenarnya itu mungkin. Anda dapat menggunakan procs / lambda untuk ini.

def test(value)
  inner = ->() {
    value * value
  }
  inner.call()
end
SuperManEver
sumber
2
Anda tidak salah, tetapi jawaban Anda dijabarkan sebagai solusi untuk mencapai metode bertingkat. Padahal pada kenyataannya Anda hanya menggunakan procs yang bukan merupakan metode. Ini adalah jawaban yang bagus di luar mengklaim untuk memecahkan "metode bersarang"
Brandon Buck
5

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"
iirekm
sumber
9
inner_method bukanlah metode, ini adalah fungsi / lambda / proc. Tidak ada contoh terkait dari kelas apa pun jadi ini bukan metode.
Sami Samhuri
2

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, innerdidefinisikan ulang setiap kali outerdipanggil. Jika Anda menginginkan perilaku ini, dan terkadang Anda mungkin menginginkannya, ini adalah cara yang baik untuk mendapatkannya.

mdmoskwa.dll
sumber
2

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_missinguntuk 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). mletdinamai menurut bentuk Common Lisp flet, kecuali fsingkatan 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.

Buang Akun
sumber
-3

:-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" 
bbozo.dll
sumber
4
Ini bukan metode bersarang ... lihat jawaban Jörg W Mittag untuk pemahaman yang jelas.
Hardik