Ruby, metodun aşırı yüklenmesini desteklemek yerine mevcut metotların üzerine yazar. Dilin neden bu şekilde tasarlandığını kimse açıklayabilir mi?
Yanıtlar:
Metod aşırı yüklemesi, aynı isim ve farklı imzalara sahip iki metot bildirilerek gerçekleştirilebilir. Bu farklı imzalar şunlar olabilir:
method(int a, int b) vs method(String a, String b)method(a) vs method(a, b)Ruby'de ( dinamik tipli dil ) veri türü bildirimi olmadığı için birinci yolu kullanarak yöntem aşırı yüklemesini başaramayız . Dolayısıyla, yukarıdaki yöntemi tanımlamanın tek yoludef(a,b)
İkinci seçenekle, yöntem aşırı yüklemesini başarabilirmişiz gibi görünebilir, ancak yapamayız. Diyelim ki farklı sayıda argümana sahip iki yöntemim var,
def method(a); end;
def method(a, b = true); end; # second argument has a default value
method(10)
# Now the method call can match the first one as well as the second one,
# so here is the problem.
Bu nedenle Ruby'nin, benzersiz bir ada sahip yöntem arama zincirinde bir yöntemi sürdürmesi gerekir.
"Aşırı yükleme", Ruby'de hiç mantıklı olmayan bir terimdir. Temelde "statik argüman tabanlı sevk" ile eşanlamlı olmakla Yakut gelmez sahip statik sevk hiç . Bu nedenle, Ruby'nin argümanlara dayalı statik gönderimi desteklememesinin nedeni, statik dağıtımı (nokta) desteklememesidir. Bağımsız değişken tabanlı veya başka türlü statik gönderimi desteklemez .
Şimdi, eğer değil aslında özellikle aşırı soran, ama belki yaklaşık dinamik argüman tabanlı sevk, daha sonra cevap: Matz bunu uygulamaya yoktu çünkü. Çünkü kimse bunu teklif etmeye zahmet etmedi. Çünkü kimse onu uygulamaya zahmet etmedi.
Genel olarak, isteğe bağlı argümanlar ve değişken uzunluklu argüman listeleri içeren bir dilde dinamik argüman tabanlı gönderim, doğru yapmak çok zordur ve anlaşılır tutmak daha da zordur . Statik argüman tabanlı gönderimi olan ve isteğe bağlı argümanlar içermeyen dillerde bile (örneğin Java gibi), bazen bir ölümlü için hangi aşırı yükün seçileceğini söylemek neredeyse imkansızdır .
C # 'da, aslında herhangi bir 3-SAT problemini aşırı yük çözünürlüğüne kodlayabilirsiniz , bu da C #' daki aşırı yük çözünürlüğünün NP-zor olduğu anlamına gelir.
Şimdi bunu , aklınızda tutmanız gereken ek zaman boyutuna sahip olduğunuz dinamik gönderim ile deneyin .
Yalnızca "gizli" sıfırıncı selfargüman üzerinden gönderilen nesne yönelimli dillerin aksine, bir prosedürün tüm argümanlarına dayalı olarak dinamik olarak gönderilen diller vardır . Örneğin Common Lisp, tüm argümanların dinamik türlerini ve hatta dinamik değerlerini gönderir. Clojure, tüm argümanların keyfi bir işlevini gönderir (BTW son derece havalı ve son derece güçlüdür).
Ancak dinamik argüman tabanlı gönderime sahip herhangi bir OO dili bilmiyorum. Martin Odersky o söyledi olabilir ama, Scala için argüman tabanlı sevk eklemeyi düşünün sadece o aynı zamanda aşırı kaldırabilirsiniz eğer ve hem kullanımları aşırı ve Java uyumlu olduğunu Scala mevcut kodla geriye doğru uyumlu olması (o özellikle salıncak ve AWT söz Java'nın oldukça karmaşık aşırı yükleme kurallarının hemen hemen her kötü karanlık köşesini uygulayan son derece karmaşık hileler oynar). Ruby'ye argümana dayalı gönderi eklemekle ilgili bazı fikirlerim vardı, ancak bunu geriye dönük olarak nasıl yapacağımı asla çözemedim.
def method(a, b = true)çalışmayacak, bu nedenle yöntem aşırı yüklemesi imkansız " kadar basit değildir. Değil; bu sadece zor. Ancak BU cevabı gerçekten bilgilendirici buldum.
Bunu yapma yeteneğini aradığınızı varsayıyorum:
def my_method(arg1)
..
end
def my_method(arg1, arg2)
..
end
Ruby bunu farklı bir şekilde destekler:
def my_method(*args)
if args.length == 1
#method 1
else
#method 2
end
end
Ortak bir model de seçenekleri bir hash olarak geçirmektir:
def my_method(options)
if options[:arg1] and options[:arg2]
#method 2
elsif options[:arg1]
#method 1
end
end
my_method arg1: 'hello', arg2: 'world'
umarım yardımcı olur
Metot aşırı yükleme, farklı argüman türleri arasında ayrım yapabileceğiniz statik yazmanın olduğu bir dilde anlamlıdır
f(1)
f('foo')
f(true)
yanı sıra farklı sayıda argüman arasında
f(1)
f(1, 'foo')
f(1, 'foo', true)
İlk ayrım yakutta yoktur. Ruby dinamik yazma veya "ördek yazma" kullanır. İkinci ayrım, varsayılan bağımsız değişkenlerle veya bağımsız değişkenlerle çalışılarak ele alınabilir:
def f(n, s = 'foo', flux_compensator = true)
...
end
def f(*args)
case args.size
when
...
when 2
...
when 3
...
end
end
Bu, Ruby'nin neden aşırı yöntem yüklemesine sahip olmadığı sorusuna yanıt vermez, ancak üçüncü taraf kitaplıklar bunu sağlayabilir.
Contracts.ruby kütüphanesi aşırı yükleme sağlar. Öğreticiden uyarlanmış örnek:
class Factorial
include Contracts
Contract 1 => 1
def fact(x)
x
end
Contract Num => Num
def fact(x)
x * fact(x - 1)
end
end
# try it out
Factorial.new.fact(5) # => 120
Bunun aslında Java'nın aşırı yüklemesinden daha güçlü olduğunu unutmayın, çünkü eşleşecek değerleri belirtebilirsiniz (örn. 1 yalnızca türleri değil, ) .
Yine de bunu kullanarak düşük performans göreceksiniz; Ne kadarını tolere edebileceğinize karar vermek için kıyaslamalar yapmanız gerekecektir.
Sık sık aşağıdaki yapıyı yaparım:
def method(param)
case param
when String
method_for_String(param)
when Type1
method_for_Type1(param)
...
else
#default implementation
end
end
Bu, nesnenin kullanıcısının clean and clear method_name: method kullanmasına izin verir. Ancak, yürütmeyi optimize etmek isterse, doğrudan doğru yöntemi çağırabilir.
Ayrıca, testinizi daha net ve daha iyi hale getirir.
sorunun neden tarafında zaten harika cevaplar var. ancak, başka çözümler arayan biri Elixir desen eşleştirme özelliklerinden esinlenen işlevsel yakut mücevherini kontrol edin .
class Foo
include Functional::PatternMatching
## Constructor Over loading
defn(:initialize) { @name = 'baz' }
defn(:initialize, _) {|name| @name = name.to_s }
## Method Overloading
defn(:greet, :male) {
puts "Hello, sir!"
}
defn(:greet, :female) {
puts "Hello, ma'am!"
}
end
foo = Foo.new or Foo.new('Bar')
foo.greet(:male) => "Hello, sir!"
foo.greet(:female) => "Hello, ma'am!"