Why doesn't ruby support method overloading?
Ruby不支持方法重载,而是覆盖现有方法。有人能解释为什么这种语言是这样设计的吗?
"重载"是一个在Ruby中根本没有意义的术语。它基本上是"基于静态参数的分派"的同义词,但Ruby根本没有静态分派。所以,Ruby不支持基于参数的静态调度的原因是它不支持静态调度period。它不支持任何类型的静态分派,无论是基于参数还是其他类型。
现在,如果您实际上不是特别询问重载,而是关于基于动态参数的调度,那么答案是:因为Matz没有实现它。因为没人愿意提出。因为没有其他人费心去实现它。
一般来说,在具有可选参数和可变长度参数列表的语言中,基于动态参数的调度很难得到正确的结果,甚至很难让人理解。即使在具有静态参数的调度和没有可选参数(例如Java)的语言中,有时几乎不可能分辨出一个凡人,超载将被挑选出来。
在C中,您实际上可以将任何3-SAT问题编码为过载分辨率,这意味着C中的过载分辨率是NP困难的。
现在,尝试使用动态调度,在那里您有额外的时间维来保存在您的头脑中。
有些语言是基于过程的所有参数动态调度的,而不是面向对象的语言,它们只在"隐藏"的第0个
但是我不知道任何一种基于动态参数调度的OO语言。Martin Odersky说,他可能会考虑向斯卡拉添加基于参数的调度,但前提是他可以同时删除重载并向后兼容现有的Scala代码,该代码使用超载和兼容Java(他特别提到Swing和AWT),它执行一些非常复杂的技巧,执行相当多的EV。JAVA的相当复杂的重载规则。对于向Ruby添加基于参数的分派,我自己也有一些想法,但是我从来没有想过如何以向后兼容的方式来实现它。
方法重载可以通过声明两个具有相同名称和不同签名的方法来实现。这些不同的签名可以是,
我们无法使用第一种方法实现方法重载,因为Ruby(动态类型语言)中没有数据类型声明。因此,定义上述方法的唯一方法是
使用第二个选项,看起来我们可以实现方法重载,但是我们不能实现。假设我有两个方法具有不同数量的参数,
1 2 3 4 5 6 | 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需要在方法查找链中维护一个具有唯一名称的方法。
我想你在寻找这样做的能力:
1 2 3 4 5 6 7 | def my_method(arg1) .. end def my_method(arg1, arg2) .. end |
Ruby以不同的方式支持这一点:
1 2 3 4 5 6 7 | def my_method(*args) if args.length == 1 #method 1 else #method 2 end end |
一个常见的模式也是以散列形式传入选项:
1 2 3 4 5 6 7 8 9 | 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 2 3 | f(1) f('foo') f(true) |
以及不同数量的参数之间
1 2 3 | f(1) f(1, 'foo') f(1, 'foo', true) |
Ruby中不存在第一个区别。Ruby使用动态类型或"duck-typing"。第二个区别可以通过默认参数或使用参数来处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def f(n, s = 'foo', flux_compensator = true) ... end def f(*args) case args.size when ... when 2 ... when 3 ... end end |
这并不能回答Ruby为什么没有方法重载的问题,但是第三方库可以提供。
Ruby库允许重载。根据本教程改编的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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的重载更强大,因为您可以指定匹配的值(例如EDCOX1(0)),而不只是类型。
不过,使用它您会看到性能下降;您必须运行基准来决定您能容忍的程度。
我经常做以下结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 | def method(param) case param when String method_for_String(param) when Type1 method_for_Type1(param) ... else #default implementation end end |
这允许对象的用户使用clean和clear方法u name:method但是如果他想优化执行,他可以直接调用正确的方法。
而且,它使您的测试更清晰和更好。
在问题的"为什么"方面已经有了很好的答案。但是,如果有人想寻找其他解决方案,请检查受elixir模式匹配功能启发的功能性RubyGem。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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!" |