Overloading in Ruby
我想在Ruby中使用重载特性,就像许多其他语言一样,但Ruby本身不支持这个特性。
我是否必须使用使用
1 2 3 4 5 6 7 8 9 10 11 | class A def foo(*args) case (args.length) when 1 do something when 2 do something-else .... end end end |
你可以看到,它确实比直接超载更丑。
我想知道是否有任何关键字或其他方式(如元编程模块)可以让我以更优雅的方式定义重载方法。
你可以尝试一些元编程来达到你的目标。
请参见以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class OverloadError < ArgumentError; end class Class =begin rdoc =end def define_overload_method( methodname, *methods ) methods.each{ | proc | define_method("#{methodname}_#{proc.arity}".to_sym, &proc ) } define_method(methodname){|*x| if respond_to?("#{methodname}_#{x.size}") send"#{methodname}_#{x.size}", *x else raise OverloadError,"#{methodname} not defined for #{x.size} parameters" end } end end class X define_overload_method :ometh, Proc.new{"Called me with no parameter" }, Proc.new{ |p1|"Called me with one parameter (#{p1.inspect})" }, Proc.new{ |p1,p2|"Called me with two parameter (#{p1.inspect}, #{p2.inspect})" } end x = X.new p '----------' p x.ometh() p x.ometh(1) p x.ometh(1,2) p x.ometh(1,2,3) #OverloadError |
号
可以使用
另一种语法是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class OverloadError < ArgumentError; end class Class def def_overload( methodname) define_method(methodname){|*x| if respond_to?("#{methodname}_#{x.size}") send"#{methodname}_#{x.size}", *x else raise OverloadError,"#{methodname} not defined for #{x.size} parameters" end } end def overload_method( methodname, proc ) define_method("#{methodname}_#{proc.arity}".to_sym, &proc ) end end class X def_overload :ometh overload_method :ometh, Proc.new{"Called me with no parameter" } overload_method :ometh, Proc.new{ |p1|"Called me with one parameter (#{p1.inspect})" } overload_method :ometh, Proc.new{ |p1,p2|"Called me with two parameter (#{p1.inspect}, #{p2.inspect})" } end |
但正如霍尔格所说:
You should try to adapt to the Ruby way. There is a reason why there is no overloading in Ruby. Methods should only do one thing, not magically decide to do vastly different things just because of different arguments. Instead try to take advantage of Duck Typing and if in doubt, use different methods with meaningful names.
号
我很好奇如何实现具有类型敏感重载的版本。这里是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | class OverloadError < ArgumentError; end class Class def def_overload( methodname) define_method(methodname){|*x| methname ="xxx" methname ="#{methodname}_#{x.size}#{x.map{|p| p.class.to_s}.join('_')}" if respond_to?(methname) send methname, *x elsif respond_to?("#{methodname}_#{x.size}") send"#{methodname}_#{x.size}", *x else raise OverloadError,"#{methodname} not defined for #{x.size} parameters" end } end def overload_method( methodname, *args, &proc ) types = [] args.each{|arg| types << arg.to_s} define_method("#{methodname}_#{proc.arity}#{types.join('_')}".to_sym, &proc ) end end class X def_overload :ometh overload_method(:ometh){"Called me with no parameter" } overload_method(:ometh, String ){ |p1|"Called me with one string parameter (#{p1.inspect})" } overload_method(:ometh ){ |p1|"Called me with one parameter (#{p1.inspect})" } overload_method(:ometh){ |p1,p2|"Called me with two parameter (#{p1.inspect}, #{p2.inspect})" } end |
。
当你打电话给
1 2 | p x.ometh(1) p x.ometh('a') |
你得到
1 2 | "Called me with one parameter (1)" "Called me with one string parameter ("a")" |
。
您可以单独测试每个参数的存在性,因为如果不通过,它们将被设置为零(假设它们是按顺序传递的!).
如果您坚持使用非常不同的参数,我建议您对每个参数使用带有符号的哈希参数。以及适当的测试。
**更新**
此外,还可以重命名使用更具体名称重载的方法,例如
1 | def perform_task_with_qualifier_1 |
很少有gems能为您的ruby代码提供这个特性。
1 2 3 4 5 6 7 8 9 10 | defn(:greet, :male) { puts"Hello, sir!" } defn(:greet, :female) { puts"Hello, ma'am!" } foo.greet(:male) =>"Hello, sir!" foo.greet(:female) =>"Hello, ma'am!" |
。
您可以从这里找到更多类似于长生不老药的模式匹配功能
1 2 3 4 5 6 7 8 9 | Contract 1 => 1 def fact x x end Contract C::Num => C::Num def fact x x * fact(x - 1) end |
这个宝石有助于纠正漂亮的防御代码。对表现有一些批评。所以基准和决定。更多示例
重载的定义特征是调度是静态发生的。在Ruby中,分派总是动态发生的,没有其他方法。因此,在Ruby中不可能重载。