[英]Why doesn't ruby support method overloading?
Ruby 不支持方法重載,而是覆蓋現有方法。 誰能解釋為什么這種語言是這樣設計的?
“重載”是一個在 Ruby 中根本沒有意義的術語。 它基本上是“基於參數的靜態調度”的同義詞,但 Ruby根本沒有靜態調度。 所以,Ruby 之所以不支持基於參數的靜態分派,是因為它不支持靜態分派,句號。 它不支持任何類型的靜態調度,無論是基於參數的還是其他的。
現在,如果您實際上並沒有特別詢問重載,而是可能詢問基於動態參數的調度,那么答案是:因為 Matz 沒有實現它。 因為沒有其他人願意提出它。 因為沒有其他人費心去實現它。
一般來說,在具有可選參數和可變長度參數列表的語言中,基於動態參數的分派很難正確,甚至更難使其易於理解。 即使在具有基於靜態參數的分派且沒有可選參數的語言中(例如 Java),有時幾乎不可能告訴普通人將選擇哪個重載。
在 C# 中,您實際上可以將任何3-SAT 問題編碼為重載解決方案,這意味着 C# 中的重載解決方案是 NP 難的。
現在嘗試使用動態調度,您可以在其中保留額外的時間維度。
有些語言可以根據過程的所有參數動態分派,而面向對象的語言僅在“隱藏的”第零個self
參數上分派。 例如,Common Lisp 調度動態類型,甚至所有參數的動態值。 Clojure 調度所有參數的任意函數(順便說一句,它非常酷且非常強大)。
但我不知道任何具有基於動態參數的調度的 OO 語言。 Martin Odersky 表示他可能會考慮在 Scala 中添加基於參數的調度,但前提是他可以同時移除重載,並且與現有的使用重載的 Scala 代碼向后兼容並與 Java 兼容(他特別提到了 Swing 和 AWT它發揮了一些極其復雜的技巧,幾乎可以執行 Java 相當復雜的重載規則的每一個令人討厭的黑暗角落案例)。 我自己有一些關於向 Ruby 添加基於參數的調度的想法,但我永遠無法弄清楚如何以向后兼容的方式來實現它。
方法重載可以通過聲明兩個具有相同名稱和不同簽名的方法來實現。 這些不同的簽名可以是,
method(int a, int b) vs method(String a, String b)
method(a) vs method(a, b)
我們無法使用第一種方式實現方法重載,因為 ruby(動態類型語言)中沒有數據類型聲明。 所以定義上述方法的唯一方法是def(a,b)
使用第二個選項,看起來我們可以實現方法重載,但我們不能。 假設我有兩種具有不同數量參數的方法,
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.
因此 ruby 需要在方法查找鏈中維護一個具有唯一名稱的方法。
我想你正在尋找這樣做的能力:
def my_method(arg1)
..
end
def my_method(arg1, arg2)
..
end
Ruby 以不同的方式支持這一點:
def my_method(*args)
if args.length == 1
#method 1
else
#method 2
end
end
一個常見的模式也是將選項作為哈希傳遞:
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'
希望有幫助
方法重載在具有靜態類型的語言中是有意義的,您可以在其中區分不同類型的參數
f(1)
f('foo')
f(true)
以及不同數量的參數之間
f(1)
f(1, 'foo')
f(1, 'foo', true)
第一個區別在紅寶石中不存在。 Ruby 使用動態類型或“鴨子類型”。 第二個區別可以通過默認參數或使用參數來處理:
def f(n, s = 'foo', flux_compensator = true)
...
end
def f(*args)
case args.size
when
...
when 2
...
when 3
...
end
end
這並沒有回答為什么 ruby 沒有方法重載的問題,但是第三方庫可以提供它。
contract.ruby庫允許重載。 改編自教程的示例:
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
請注意,這實際上比 Java 的重載更強大,因為您可以指定要匹配的值(例如1
),而不僅僅是類型。
但是,您會看到使用此功能會降低性能; 你將不得不運行基准來決定你能容忍多少。
我經常做以下結構:
def method(param)
case param
when String
method_for_String(param)
when Type1
method_for_Type1(param)
...
else
#default implementation
end
end
這允許對象的用戶使用干凈清晰的方法名:方法但是如果他想優化執行,他可以直接調用正確的方法。
此外,它使您的測試更清晰和更好。
關於問題的原因已經有了很好的答案。 但是,如果有人在尋找其他解決方案,請查看受 Elixir 模式匹配功能啟發的功能性紅寶石gem。
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!"
我遇到了對 Ruby 的創造者 Yukihiro Matsumoto(又名“Matz”)的精彩采訪。 順便說一句,他在那里解釋了他的推理和意圖。 這是對@nkm 對問題的出色示例的一個很好的補充。 我已經強調了回答你關於為什么Ruby 是這樣設計的問題的部分:
正交與和諧
Bill Venners:Dave Thomas 還聲稱,如果我要求你添加一個正交的特征,你不會這樣做。 你想要的是和諧的東西。 這意味着什么?
Yukihiro Matsumoto:我相信一致性和正交性是設計的工具,而不是設計的主要目標。
Bill Venners:在這種情況下,正交性意味着什么?
Yukihiro Matsumoto:正交性的一個例子是允許小特征或語法的任意組合。 例如,C++ 支持函數的默認參數值和基於參數的函數名重載。 兩者都是一種語言的好特性,但因為它們是正交的,你可以同時應用它們。 編譯器知道如何同時應用兩者。 如果它不明確,編譯器將標記一個錯誤。 但是如果我看代碼,我也需要用我的大腦來應用這個規則。 我需要猜測編譯器是如何工作的。 如果我是對的,而且我足夠聰明,那沒問題。 但如果我不夠聰明,而且我真的不夠聰明,就會引起混亂。 結果對於一個普通人來說是出乎意料的。 這是正交性有多糟糕的一個例子。
資料來源:“Ruby 的哲學”,與 Yukihiro Matsumoto 的對話,Bill Venners 的第一部分,2003 年 9 月 29 日,網址: https ://www.artima.com/intv/ruby.html
靜態類型語言支持方法重載,這涉及到它們在編譯時的綁定。 另一方面,Ruby 是一種動態類型語言,根本不支持靜態綁定。 在具有可選參數和可變長度參數列表的語言中,也很難確定在基於動態參數的調度期間將調用哪個方法。 此外,Ruby 是用 C 實現的,它本身不支持方法重載。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.