Yöntemlerin içinde Yöntemlere sahip olmak mümkün müdür?


89

Bir yöntemin içinde bir yöntemim var. İç yöntem, çalıştırılan değişken bir döngüye bağlıdır. Bu kötü bir fikir mi?


2
Kod örneğini veya en azından yapmaya çalıştığınız şeyin mantıksal bir eşdeğerini paylaşabilir misiniz?
Aaron Scruggs

Yanıtlar:


165

GÜNCELLEME: Bu yanıt son zamanlarda biraz ilgi aldık görünüyor beri olduğu işaret etmek istediği tartışma için Yakut sorun izleyicide üzerinde kaldırmak özellik yani için, burada tartışılan korusun bir yöntem vücudun içinde yöntem tanımlarını sahip .


Hayır, Ruby'de yuvalanmış yöntemler yoktur.

Bunun gibi bir şey yapabilirsiniz:

class Test1
  def meth1
    def meth2
      puts "Yay"
    end
    meth2
  end
end

Test1.new.meth1

Ancak bu iç içe geçmiş bir yöntem değildir . Tekrar ediyorum: Yakut yoktur iç içe yöntemleri.

Bu, dinamik bir yöntem tanımıdır. Koştuğun zaman meth1, bedeni meth1idam edilecek. Gövde sadece adlandırılmış bir yöntemi tanımlar meth2, bu yüzden bir meth1kez çalıştırdıktan sonra,meth2 .

Ama nerede meth2tanımlanıyor? Eh, belli ki oluyor değil orada beri, iç içe geçmiş bir yöntem olarak tanımlanan hiçbir vardır Ruby iç içe yöntemleri. Aşağıdakilerin bir örnek yöntemi olarak tanımlanır Test1:

Test1.new.meth2
# Yay

Ayrıca, her çalıştırdığınızda açıkça yeniden tanımlanacaktır 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

Kısacası: hayır, Yakut gelmez iç içe yöntemleri destekler.

Ruby'de yöntem gövdelerinin kapanış olamayacağını, yalnızca blok gövdelerinin yapabileceğini unutmayın. Bu hemen hemen ortadan kaldırır bile bu yana iç içe yöntemleri için başlıca kullanım amaçlarından, eğer Yakut iç içe yöntemleri desteklenen, iç içe geçmiş yönteminde dış yöntemin değişkenleri kullanamadı.


GÜNCELLEME DEVAM EDİYOR: Daha sonraki bir aşamada, bu sözdizimi, Ruby'ye iç içe geçmiş yöntemler eklemek için yeniden kullanılabilir, bu da benim tanımladığım şekilde davranır: kapsama yöntemlerine göre kapsama alınır, yani içerme yöntemlerinin dışında görünmez ve erişilemez. vücut. Ve muhtemelen, içerme yöntemlerinin sözlü kapsamına erişebilirlerdi. Bununla birlikte, yukarıda bağladığım tartışmayı okursanız, matz'in iç içe geçmiş yöntemlere büyük ölçüde karşı olduğunu (ancak yine de yuvalanmış yöntem tanımlarını kaldırmak için) görebilirsiniz.


6
Bununla birlikte, DRYness için bir yöntemde bir kapanış lambda'nın nasıl oluşturulacağını veya özyinelemenin nasıl çalıştırılacağını da gösterebilirsiniz.
Phrogz

119
Ruby'nin iç içe geçmiş yöntemleri olmayabileceği hissine kapılıyorum.
Mark Thomas

16
@Mark Thomas: Ruby'nin iç içe geçmiş yöntemlere sahip olmadığını söylemeyi unuttum mu? :-) Ciddi: Ben bu cevabı yazdım anda zaten Yakut iddia her biri üç cevaplar vardı yapar iç içe yöntemleri vardır. Hatta bu cevaplardan bazıları, bariz bir şekilde yanlış olmasına rağmen olumlu oylar aldı. Hatta biri, yanlış olmasına rağmen yine OP tarafından kabul edildi. Cevabın Ruby'nin iç içe geçmiş yöntemleri desteklediğini kanıtlamak için kullandığı kod parçacığı, aslında tam tersini kanıtlıyor, ancak görünüşe göre ne yukarı oy verenler ne de OP kontrol etmeye zahmet etmedi. Bu yüzden her yanlış cevap için bir doğru cevap verdim. :-)
Jörg W Mittag

10
Bunların hepsinin Kernel için tabloları değiştiren talimatlar olduğunu ve yöntemlerin, sınıfların ve modüllerin sadece tablolardaki girişler olduğunu ve gerçekte gerçek olmadığını anladığınızda, Matrix'in neye benzediğini gördüğünde Neo gibi olursunuz. O zaman gerçekten felsefi olabilir ve iç içe geçmiş yöntemlerin yanı sıra, yöntemlerin bile olmadığını söyleyebilirsin. Ajanlar bile yok. Matristeki programlardır. Yediğin o sulu biftek bile bir masanın girişidir.
mydoghasworms

3
Yöntem yok, kodunuz The Matrix'te sadece bir simülasyon
bbozo

13

Aslında mümkün. Bunun için procs / lambda kullanabilirsiniz.

def test(value)
  inner = ->() {
    value * value
  }
  inner.call()
end

2
Yanılıyorsunuz, ancak cevabınız iç içe geçmiş yöntemlere ulaşmak için bir çözüm olarak ifade ediliyor. Gerçekte sadece yöntem olmayan prosedürleri kullandığınızda. "İç içe geçmiş yöntemleri"
Brandon Buck

5

Hayır, hayır, Ruby iç içe geçmiş yöntemlere sahiptir. Şuna göz at:

def outer_method(arg)
    outer_variable = "y"
    inner_method = lambda {
      puts arg
      puts outer_variable
    }
    inner_method[]
end

outer_method "x" # prints "x", "y"

9
inner_method bir yöntem değil, bir işlev / lambda / proc. Herhangi bir sınıfın ilişkili bir örneği olmadığından bu bir yöntem değildir.
Sami Samhuri

2

Bunun gibi bir şey yapabilirsin

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

Bu, yöntemler arasında kapsam paylaşımı gerektiren DSL'ler yazmak gibi şeyler yaparken kullanışlıdır. Ama aksi takdirde, başka bir şey yapmaktan çok daha iyi olursunuz çünkü diğer cevapların da söylediği gibi inner, her outerçağrıldığında yeniden tanımlanır . Bu davranışı istiyorsanız ve bazen isteyebilirsiniz, bu onu elde etmenin iyi bir yoludur.


2

Ruby'nin yolu, bazı kullanıcıların "Bu nasıl oluyor da işe yarıyor?" Daha önce Rake veya Rails kullandıysanız, bu tür şeyler görmüşsünüzdür.

İşte böyle bir 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

Bunun yaptığı şey, bir sınıf oluşturan ve onu bir bloğa geçiren üst düzey bir yöntemi tanımlamaktır. Sınıf, method_missingseçtiğiniz ada sahip bir yöntemi varmış gibi davranmak için kullanır . Sağlamanız gereken lambdayı çağırarak yöntemi "uygular". Nesneyi tek harfli bir adla adlandırarak, gerektirdiği fazladan yazma miktarını en aza indirebilirsiniz (bu, Rails'in içinde yaptığı şeyle aynıdır schema.rb). "function" anlamına gelen yer dışında mlet, Common Lisp formundan sonra adlandırılır ,fletfm anlamına gelen "method" anlamına .

Bunu şu şekilde kullanırsın:

def outer
   mlet :inner, ->(x) { x*2 } do |c|
     c.inner 12
   end
end

Ek yuvalama olmadan birden fazla iç işlevin tanımlanmasına izin veren benzer bir mekanizma yapmak mümkündür, ancak bu, Rake veya Rspec uygulamasında bulabileceğiniz türde daha da çirkin bir hack gerektirir. Rspec'in let!çalışmalarının size böylesine korkunç bir iğrençlik yaratma yolunda size uzun bir yol sağlayacağını anlamak .


-3

:-D

Ruby iç içe geçmiş yöntemlere sahiptir, ancak onlar beklediğiniz şeyi yapmaz

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" 

4
Bu iç içe bir yöntem değildir ... Net bir anlayış için Jörg W Mittag'ın cevabına bakın.
Hardik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.