为啥 ruby 不支持方法重载?
Posted
技术标签:
【中文标题】为啥 ruby 不支持方法重载?【英文标题】:Why doesn't ruby support method overloading?为什么 ruby 不支持方法重载? 【发布时间】:2012-03-11 12:00:38 【问题描述】:Ruby 不支持方法重载,而是覆盖现有方法。谁能解释一下为什么这种语言是这样设计的?
【问题讨论】:
【参考方案1】:“重载”是一个在 Ruby 中根本没有意义的术语。它基本上是“基于参数的静态调度”的同义词,但 Ruby 没有静态调度根本。所以,Ruby 之所以不支持基于参数的静态分派,是因为它不支持静态分派,句号。它不支持任何类型的静态调度,无论是基于参数的还是其他的。
现在,如果您实际上不是专门询问重载,而可能是动态基于参数的调度,那么答案是:因为 Matz 没有实现它.因为没有其他人愿意提出它。因为没有其他人费心去实现它。
一般来说,在具有可选参数和可变长度参数列表的语言中,基于动态参数的调度非常难以正确,甚至更难保持可以理解。即使在具有基于 static 参数的分派且没有可选参数的语言中(例如 Java),有时也几乎无法判断一个普通人,哪个重载是将被选中。
在 C# 中,您实际上可以将 任何 3-SAT 问题编码为重载解决方案,这意味着 C# 中的重载解决方案是 NP-hard。
现在尝试使用 dynamic 调度,您可以在其中保留额外的时间维度。
有些语言可以根据过程的所有参数动态分派,而面向对象的语言只在“隐藏的”第零个self
参数上分派。例如,Common Lisp 调度动态类型,甚至所有参数的动态值。 Clojure 调度所有参数的任意函数(顺便说一句,这非常酷且非常强大)。
但我不知道任何具有基于动态参数的调度的 OO 语言。 Martin Odersky 说他可能考虑将基于参数的调度添加到 Scala,但只有如果他可以同时移除重载并且向后- 与使用重载的现有 Scala 代码兼容并与 Java 兼容(他特别提到了 Swing 和 AWT,它们发挥了一些极其复杂的技巧,几乎可以执行 Java 相当复杂的重载规则的每一个令人讨厌的黑暗角落案例)。我自己有一些关于向 Ruby 添加基于参数的调度的想法,但我永远无法弄清楚如何以向后兼容的方式来实现。
【讨论】:
这是正确的答案。接受的答案过于简单化了。 C# DOES 有命名参数和可选参数,并且仍然实现重载,所以它不像“def method(a, b = true)
不起作用,因此方法重载是不可能的”那么简单。它不是;这很难。然而,我发现这个答案非常有用。
@tandrewnichols:只是为了让大家了解一下 C# 中的“困难”重载解决方案是如何……可以将任何 3-SAT 问题编码为 C# 中的重载解决方案,并让编译器在编译时解决它,从而在 C# NP-hard 中进行重载解析(已知 3-SAT 是 NP-complete)。现在想象一下,不必在编译时对每个调用站点执行一次,而是在运行时对每个方法调用的每个方法调用执行一次。
@JörgWMittag 您能否在重载解决机制中包含一个显示 3-SAT 问题编码的链接?
@MrBones: blogs.msdn.com/b/ericlippert/archive/2007/03/28/…
看起来“重载”并不是“基于静态参数的调度”的同义词。基于静态参数的调度只是最常见的重载实现。重载是一个与实现无关的术语,意思是“相同的方法名称,但在相同的范围内有不同的实现”。【参考方案2】:
方法重载可以通过声明两个具有相同名称和不同签名的方法来实现。这些不同的签名可以是,
-
具有不同数据类型的参数,例如:
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 需要在方法查找链中维护一个具有唯一名称的方法。
【讨论】:
@Jörg W Mittag 的答案深埋在地下,绝对值得一读。 而@Derek Ekins 的回答被埋得更深了,提供了另一种选择 注意未来的 rubyists ... FWIW 你可以通过合同 gem egonschiele.github.io/contracts.ruby/#method-overloading 做到这一点 这是@Jörg W Mittag 答案的链接:***.com/a/9380268/4880924【参考方案3】:我想你正在寻找这样做的能力:
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'
希望有帮助
【讨论】:
+1 提供了我们许多人只想知道的:如何在 Ruby 方法中使用可变数量的参数。 此答案可能会受益于有关可选参数的其他信息。 (也许还有命名参数,现在这些都是一回事。)【参考方案4】:方法重载在具有静态类型的语言中很有意义,您可以在其中区分不同类型的参数
f(1)
f('foo')
f(true)
以及不同数量的参数之间
f(1)
f(1, 'foo')
f(1, 'foo', true)
第一个区别在 ruby 中不存在。 Ruby 使用动态类型或“鸭子类型”。第二个区别可以通过默认参数或使用参数来处理:
def f(n, s = 'foo', flux_compensator = true)
...
end
def f(*args)
case args.size
when
...
when 2
...
when 3
...
end
end
【讨论】:
这与强类型无关。毕竟,Ruby 是强类型的。【参考方案5】:这并没有回答为什么 ruby 没有方法重载的问题,但是第三方库可以提供。
contracts.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
),而不仅仅是类型。
您会发现使用此功能会降低性能;您将不得不运行基准测试来决定您可以容忍多少。
【讨论】:
在具有任何类型 IO 的实际应用程序中,您只会有 0.1-10%(取决于哪种 IO)的减速。【参考方案6】:我经常做如下结构:
def method(param)
case param
when String
method_for_String(param)
when Type1
method_for_Type1(param)
...
else
#default implementation
end
end
这允许对象的用户使用干净和清晰的方法名:方法 但是如果他想优化执行,他可以直接调用正确的方法。
此外,它使您的测试更清晰、更好。
【讨论】:
【参考方案7】:对于问题的原因,已经有了很好的答案。但是,如果有人在寻找其他解决方案,请查看受 Elixir pattern matching 功能启发的 functional-ruby 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!"
【讨论】:
【参考方案8】:我对 Ruby 的创造者 Yukihiro Matsumoto(又名“Matz”)进行了一次精彩的采访。顺便说一句,他在那里解释了他的推理和意图。这是对@nkm 对问题的出色示例的一个很好的补充。我已经强调了回答你关于为什么 Ruby 是这样设计的问题的部分:
正交与和谐
Bill Venners:Dave Thomas 还声称 如果我要求你添加一个 正交的特征,你不会做。你想要的是 和谐的东西。这是什么意思?
Yukihiro Matsumoto:我相信一致性和正交性是工具 设计,而不是设计的主要目标。
Bill Venners:正交性在这种情况下意味着什么?
Yukihiro Matsumoto:正交性的一个例子是允许任何 小功能或语法的组合。 例如,C++ 支持 函数的默认参数值和重载 基于参数的函数名称。 两者都是很好的特性 一种语言,但由于它们是正交的,因此您可以同时应用 同时。编译器知道如何同时应用两者。如果 这是模棱两可的,编译器会标记一个错误。但如果我看 代码,我也需要用我的大脑来应用规则。我需要猜测如何 编译器工作。如果我是对的,而且我足够聪明,那就不是 问题。但如果我不够聪明,而且我真的不够聪明,它会导致 混乱。结果对于普通人来说将是出乎意料的。 这个 是正交性有多糟糕的一个例子。
资料来源:《Ruby 的哲学》,与松本幸弘的对话,第一部分 作者:Bill Venners,2003 年 9 月 29 日:https://www.artima.com/intv/ruby.html
【讨论】:
以上是关于为啥 ruby 不支持方法重载?的主要内容,如果未能解决你的问题,请参考以下文章