在Ruby中重载

Sho*_* Ya 10 ruby overloading

我想像许多其他语言一样在Ruby中使用重载功能,但Ruby本身不支持此功能.

我是否必须使用定义带*args参数的方法的方式来实现它,并确定方法内参数的数量和类型?有些人喜欢:

class A
    def foo(*args)
        case (args.length)
        when 1
            do something
        when 2
            do something-else
        ....
        end
    end
end
Run Code Online (Sandbox Code Playgroud)

你可以看到,它比直接重载更难看.

我想知道是否有任何关键字或其他方式(如元编程模块)可以让我以更优雅的方式定义重载方法.

Art*_*rth 5

你可以分别测试每个参数的存在,因为它们被设置为nil如果没有传递(假设它们按顺序传递!).

如果你坚持使用非常不同的参数,我建议用你想要的每个参数的符号哈希参数,并进行适当的测试.

**更新**

您还可以使用更具体的名称重命名重载的方法,例如

def perform_task_with_qualifier_1
Run Code Online (Sandbox Code Playgroud)


knu*_*nut 5

您可以尝试一些元编程来达到目标​​.

请参阅以下代码:

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

您可以使用定义重载方法define_overload_method.参数是方法名称和过程列表.methodname创建该方法并调用相应的方法.哪个方法由参数个数决定(非类型!).

另一种语法是:

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

def_overload为重载方法定义框架,overload_method定义一个'overload-method'.

但正如霍尔格已经提到的那样:

你应该尝试适应Ruby的方式.有一个原因是Ruby中没有重载.方法应该只做一件事,而不是因为不同的论点而神奇地决定做出截然不同的事情.而是尝试利用Duck Typing,如果有疑问,请使用具有有意义名称的不同方法.


我很好奇如何实现具有类型敏感重载的版本.这里是:

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

当你打电话给它

p x.ometh(1)
p x.ometh('a')
Run Code Online (Sandbox Code Playgroud)

你得到

    "Called me with one parameter (1)"
    "Called me with one string parameter (\"a\")"
Run Code Online (Sandbox Code Playgroud)