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

时间:2012-02-21 06:22:50

标签: ruby

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

8 个答案:

答案 0 :(得分:199)

“重载”这个术语在Ruby中甚至没有意义。它基本上是“基于静态参数的调度”的同义词,但Ruby 静态调度 。因此,Ruby不支持基于参数的静态调度的原因是因为它不支持静态调度,周期。它不支持静态调度任何类型的,无论是基于参数还是其他。

现在,如果你实际上专门询问重载,但可能关于动态基于参数的调度,那么答案是:因为Matz没有实现它。因为没有其他人打算提出它。因为没有其他人愿意实施它。

通常,在带有可选参数和可变长度参数列表的语言中基于动态参数的调度是非常很难正确,甚至更难来保持这是可以理解的。即使在基于 static 基于参数的调度并且没有可选参数(例如Java)的语言中,有时几乎不可能告诉一个凡人,哪个重载是将被挑选。

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

现在尝试使用动态调度,您需要额外的时间维度。

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

但我不知道任何基于动态参数的调度的OO语言。 Martin Odersky说他可能考虑将基于参数的调度添加到Scala,但如果他可以同时删除重载倒退兼容使用现有的Scala代码,它使用重载并与Java兼容(他特别提到了Swing和AWT,它们运行一些极其复杂的技巧,几乎可以处理Java相当复杂的重载规则的每个令人讨厌的黑暗角落)。我自己有一些关于在Ruby中添加基于参数的调度的想法,但我从来没有想过如何以向后兼容的方式来实现它。

答案 1 :(得分:151)

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

  1. 具有不同数据类型的参数,例如:method(int a, int b) vs method(String a, String b)
  2. 可变数量的参数,例如:method(a) vs method(a, b)
  3. 我们无法使用第一种方式实现方法重载,因为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需要在方法查找链中使用唯一名称维护一个方法。

答案 2 :(得分:80)

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

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'

希望有所帮助

答案 3 :(得分: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

答案 4 :(得分: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),而不仅仅是类型。

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

答案 5 :(得分: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:方法 但如果他想优化执行,他可以直接调用正确的方法。

此外,它使你的测试清理器和更好。

答案 6 :(得分:0)

对于问题的原因,已经有了很好的答案。但是,如果有人在寻找其他解决方案,请查看functional-rub是受Elixir pattern matching功能启发的宝石。

     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!"   

答案 7 :(得分:0)

我对 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