为什么红宝石不支持方法重载?


146

Ruby不支持方法重载,而是覆盖现有方法。谁能解释为什么用这种方式设计语言?

Answers:


166

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

  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.

因此,ruby需要在方法查找链中维护一个具有唯一名称的方法。


22
@JörgW Mittag的答案深埋在下面,绝对值得一读。
user2398029 '11

1
@Derek Ekins的答案被埋得更深,提供了另一种选择
Cyril Duchon-Doris

注意未来的红宝石主义者... FWIW,您可以通过合同宝石做到这一点egonschiele.github.io/contracts.ruby/#method-overloading
EngineerDave

214

“重载”是一个在Ruby中甚至根本没有意义的术语。它基本上是“静态的基于论证的调度”的代名词,但红宝石不具备静态调度可言。因此,Ruby不基于参数支持静态分派的原因是因为它不支持period的静态分派。它不支持任何类型的静态分派,无论是基于参数还是其他方式。

现在,如果您不是真正地在问有关重载的问题,而是关于基于参数的动态分派的问题,那么答案是:因为Matz没有实现它。因为没有人愿意提出建议。因为没有人愿意执行它。

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

在C#中,您实际上可以将任何 3-SAT问题编码为重载分辨率,这意味着C#中的重载分辨率是NP-hard。

现在尝试使用动态调度,在该调度中您将拥有更多的时间维度。

有一些语言可以根据过程的所有参数动态地进行分派,而与面向对象的语言相反,它们仅在“隐藏的”第零个self参数上进行分派。例如,Common Lisp调度动态类型,甚至所有参数的动态值。Clojure调度所有参数的任意函数(BTW非常酷,而且功能非常强大)。

但是我不知道任何基于动态参数调度的OO语言。马丁·奥德斯基说,他可能会考虑增加基于论证的派遣斯卡拉,但只有当他可以删除在同一时间超载向后兼容都与现有的Scala代码,使用重载和与Java兼容(他特别提到Swing和AWT在Java相当复杂的重载规则的每一个讨厌的暗角情况下,它们都发挥了一些极其复杂的技巧。我本人对向Ruby添加基于参数的分派有一些想法,但是我永远无法弄清楚如何以向后兼容的方式来实现它。


5
这是正确的答案。接受的答案过于简单。C#确实具有命名参数和可选参数,并且仍然实现重载,因此它不像“ def method(a, b = true)不起作用,因此方法重载是不可能的” 那样简单。不是; 这很难。我发现这个答案确实很有用。
tandrewnichols 2013年

1
@tandrewnichols:只是为了让大家了解一下C#中的“困难”重载分辨率是什么……可以将任何3-SAT问题编码为C#中的重载分辨率,并让编译器在编译时加以解决,从而在C#NP中实现重载分辨率硬(已知3-SAT是NP完整的)。现在想象一下,不必在编译时每个调用站点执行一次操作,而是在运行时为每个方法调用每个方法调用执行一次操作
约尔格W¯¯米塔格

1
@JörgWMittag您能否在超载解决机制中包括一个显示3-SAT问题编码的链接?
Squidly


2
看起来“重载”不是“基于静态参数的调度”的同义词。基于静态参数的分派只是重载的最常见实现。重载是与实现无关的术语,意思是“相同的方法名称,但是在同一范围内的不同实现”。
snovity

85

我认为您正在寻找执行此操作的能力:

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'

希望能有所帮助


15
+1提供了我们许多人想知道的内容:如何在Ruby方法中使用可变数量的参数。
ashes999

3
该答案可能会受益于有关可选参数的其他信息。(现在也许是论点了,因为这已经是一回事了。)
Ajedi32

9

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

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 强类型的。
约尔格W¯¯米塔格

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

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

但是您会发现使用此功能会降低性能;您将必须运行基准测试来确定您可以忍受的程度。


1
在具有任何类型IO的实际应用中,您的速度只会降低0.1-10%(取决于哪种IO)。
水联线

1

我经常做以下结构:

def method(param)
    case param
    when String
         method_for_String(param)
    when Type1
         method_for_Type1(param)

    ...

    else
         #default implementation
    end
end

这允许对象的用户使用干净的method_name:method,但是如果他想优化执行,则可以直接调用正确的方法。

而且,它使您的测试更加清晰和更好。


1

对于问题的原因,已经有了很好的答案。但是,如果有人在寻找其他解决方案,请查看功能红宝石,这是受Elixir 模式匹配功能启发的。

 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!"   
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.