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

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

53570 次浏览

我假设你正在寻找这样做的能力:

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'

希望能帮上忙

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

f(1)
f('foo')
f(true)

以及不同数量的参数之间

f(1)
f(1, 'foo')
f(1, 'foo', true)

第一个区别在红宝石中不存在。Ruby 使用动态类型或“ Duck 类型”。第二个区别可以通过默认参数或使用参数来处理:

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




def f(*args)
case args.size
when
...
when 2
...
when 3
...
end
end

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

  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 需要在方法查找链中维护一个具有唯一名称的方法。

“重载”这个词在 Ruby 中根本没有意义。它基本上是“基于参数的静态分派”的同义词,但 Ruby 不支持 静态分派 完全没有。所以,Ruby 不支持基于参数的静态分派的原因是因为它不支持静态分派,句号。它不支持 什么都行的静态分派,无论是基于参数还是其他方式。

现在,如果您是 没有,实际上特别询问关于重载的问题,但是可能是关于基于 充满活力参数的分派的问题,那么答案是: 因为 Matz 没有实现它。因为其他人都懒得提出来。因为其他人都懒得去实现它。

一般来说,在一种具有可选参数和可变长参数列表的语言中,基于动态参数的分派是 非常很难做到的,甚至是 用力点也很难让人理解。即使在使用 静电干扰基于参数的分派和没有可选参数的语言中(例如 Java) ,有时候对于一个普通人来说几乎不可能知道,哪个重载将被选中。

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

现在尝试使用 充满活力调度,在这里您可以保留额外的时间维度。

有些语言基于过程的所有参数进行动态分派,而面向对象语言只分派“隐藏”的第零个 self参数。例如,Common Lisp 分派所有参数的动态类型甚至动态值。Clojure 分派所有参数的任意函数(顺便说一句,这个函数非常酷,也非常强大)。

但是我不知道任何具有基于动态参数的分派的 OO 语言。Martin Odersky 说,他考虑在 Scala 中加入基于参数的分派,但是如果 只有能够同时去除重载,那么 还有就能够向后兼容现有的使用重载的 Scala 代码和 Java 兼容(他特别提到了 Swing 和 AWT,这两个代码使用了一些极其复杂的技巧,运用了 Java 相当复杂的重载规则中几乎所有令人讨厌的黑暗角落的情况)。关于在 Ruby 中添加基于参数的分派,我自己也有一些想法,但是我从来不知道如何以向后兼容的方式来实现。

我经常这样做:

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 但是如果他想优化执行,他可以直接调用正确的方法。

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

这并不能回答为什么 Ruby 没有方法重载的问题,但是第三方库可以提供它。

合同 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) ,而不仅仅是类型。

但是,您将看到使用这种方法会降低性能; 您必须运行基准测试来决定您能够忍受多大程度的性能下降。

关于为什么这个问题已经有了很好的答案。然而,如果任何人寻找其他解决方案签出 功能性红宝石宝石,这是灵感来自长生不老药 模式匹配功能。

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

我无意中发现了这个关于松本行弘的采访(也就是。“ Matz”) ,Ruby 的创造者。顺便说一句,他在那里解释了他的理由和意图。这是对@nkm 这个问题的极好例证的一个很好的补充。我已经强调了回答你关于 为什么 Ruby 的问题的部分是这样设计的:

正交与和谐

比尔 · 维纳斯: 戴夫 · 托马斯也声称,如果我要求你添加一个 特性是正交的,你不会这样做。 你想要的是 和谐的东西,那是什么意思?

松本行弘: 我相信一致性和正交性是工具 而不是设计的主要目标。

比尔: 在这种情况下,正交性意味着什么?

松本行弘: 正交性的一个例子是允许任何 例如,C + + 支持 函数的默认参数值和 基于参数的函数名。 这两个都是很好的特性 一种语言,但是因为它们是正交的,所以可以在 编译器知道如何同时应用两者。如果 它是模棱两可的,编译器会标记一个错误 代码,我也需要用我的大脑来运用这个规则。我需要猜测如何运用 编译器工作。如果我是正确的,而且我足够聪明,它是不 问题。但是如果我不够聪明,我真的不够聪明,它会导致 混乱 。结果将是 出乎意料为一个普通人。 这 是正交性如何不好的一个例子

资料来源: 《红宝石的哲学》 ,与松本行弘的对话,第一部分 作者: Bill Venners,2003年9月29日,网址: http://www.artima.com/intv/ruby.html/rel = “ nofollow norefrer”https://www.artima.com/intv/ruby.html

静态类型语言支持方法重载,这涉及到它们在编译时的绑定。另一方面,Ruby 是一种动态类型语言,根本不支持静态绑定。在具有可选参数和可变长度参数列表的语言中,也很难确定在基于参数的动态分派期间将调用哪个方法。此外,Ruby 是用 C 语言实现的,而 C 语言本身不支持方法重载。