为什么Ruby不支持方法重载?

Ram*_*mbo 142 ruby

而不是支持方法重载Ruby覆盖现有方法.任何人都可以解释为什么语言是这样设计的?

Jör*_*tag 204

"重载"这个术语在Ruby中根本就没有意义.它基本上是"静态的基于论证的调度"的代名词,但红宝石不具备静态调度可言.因此,Ruby不支持基于参数的静态调度的原因是因为它不支持静态调度,周期.它不支持任何类型的静态分派,无论是基于参数还是其他.

现在,如果你实际上并没有特别询问有关重载的问题,而是关于基于动态参数的调度,那么答案是:因为Matz没有实现它.因为没有其他人打算提出它.因为没有其他人费心去实施它.

在与可选参数和可变长度参数列表中选择语言一般情况下,基于动态参数的分派,是非常难以得到正确的,甚至更难保持它可以理解的.即使在具有基于静态参数的调度和没有可选参数(例如Java)的语言中,有时几乎不可能分辨出一个凡人,哪个过载将被选中.

在C#中,您实际上可以将任何 3-SAT问题编码为重载决策,这意味着C#中的重载决策是NP难的.

现在尝试使用动态调度,您可以在其中保留额外的时间维度.

有些语言基于过程的所有参数动态调度,而不是面向对象的语言,它只在"隐藏"的第0个self参数上进行调度.例如,Common Lisp会调度所有参数的动态类型甚至动态值.Clojure调度所有参数的任意函数(BTW非常酷且极其强大).

但我不知道任何基于动态参数的调度的OO语言.马丁·奥德斯基说,他可能会考虑增加基于论证的派遣斯卡拉,但只有当他可以删除在同一时间超载向后兼容都与现有的使用过载和与Java兼容的Scala代码(他特别提到Swing和AWT它发挥了一些极其复杂的技巧,几乎可以解决Java相当复杂的重载规则中令人讨厌的黑暗角落的问题.我自己有一些关于在Ruby中添加基于参数的调度的想法,但我从来没有想过如何以向后兼容的方式来实现它.

  • 这是正确的答案.接受的答案过于简单化了.C#DOES已经命名了参数和可选参数,并且仍然实现了重载,因此它并不像"`def方法(a,b = true)`不起作用那么简单,因此方法重载是不可能的." 不是; 这很难.然而,我发现这个答案真的很有用. (5认同)
  • [Wikipedia:Dynamic Dispatch](http://en.wikipedia.org/wiki/Dynamic_dispatch) (3认同)
  • 它看起来不像"重载"是"基于静态参数的调度"的同义词.基于静态参数的调度只是最常见的重载实现.重载是一个与实现无关的术语,意思是"相同的方法名称,但在同一范围内的实现不同". (2认同)

nkm*_*nkm 158

可以通过声明具有相同名称和不同签名的两个方法来实现方法重载.这些不同的签名可以是,

  1. 具有不同数据类型的参数,例如: method(int a, int b) vs method(String a, String b)
  2. 可变数量的参数,例如: 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.
Run Code Online (Sandbox Code Playgroud)

所以ruby需要在方法查找链中使用唯一名称维护一个方法.

  • @JörgWMittag的答案,埋在远处,绝对值得一读. (22认同)
  • 而@Derek Ekins 的答案,甚至更深,提供了另一种选择 (2认同)

Der*_*ins 84

我认为你正在寻找这样做的能力:

def my_method(arg1)
..
end

def my_method(arg1, arg2)
..
end
Run Code Online (Sandbox Code Playgroud)

Ruby以不同的方式支持它:

def my_method(*args)
  if args.length == 1
    #method 1
  else
    #method 2
  end
end
Run Code Online (Sandbox Code Playgroud)

一个常见的模式也是将选项作为哈希传递:

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'
Run Code Online (Sandbox Code Playgroud)

希望有所帮助

  • +1用于提供我们许多人想要知道的内容:如何在Ruby方法中使用可变数量的参数. (14认同)
  • 这个答案可能会受益于有关可选参数的其他信息.(也许还有名称参数,现在这些都是一件事.) (3认同)

bje*_*lli 9

方法重载在具有静态类型的语言中是有意义的,您可以在其中区分不同类型的参数

f(1)
f('foo')
f(true)
Run Code Online (Sandbox Code Playgroud)

以及不同数量的参数之间

f(1)
f(1, 'foo')
f(1, 'foo', true)
Run Code Online (Sandbox Code Playgroud)

红宝石中不存在第一个区别.Ruby使用动态类型或"鸭子打字".第二个区别可以通过默认参数或使用参数来处理:

def f(n, s = 'foo', flux_compensator = true)
   ...
end


def f(*args)
  case args.size
  when  
     ...
  when 2
    ...
  when 3
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)


Kel*_*vin 8

这并没有回答为什么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
Run Code Online (Sandbox Code Playgroud)

请注意,这实际上比Java的重载更强大,因为您可以指定要匹配的值(例如1),而不仅仅是类型.

尽管如此,你会看到性能下降; 你必须运行基准来决定你能忍受多少.