11

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

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

class A
    def foo(*args)
        case (args.length)
        when 1
            do something
        when 2
            do something-else
        ....
        end
    end
end

可以看到,确实比直接重载难看。

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

4

4 回答 4

5

您可以分别测试每个参数是否存在,因为如果未通过,它们将设置为 nil(假设它们按顺序传递!)。

如果您坚持使用非常不同的论点,我建议您使用带有符号的散列论点来表示您想要的每个论点......并进行适当的测试。

** 更新 **

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

def perform_task_with_qualifier_1
于 2012-04-14T20:06:29.450 回答
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

您可以使用 定义重载方法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

def_overload定义重载方法的框架,overload_method定义一个“重载方法”。

但正如Holger 已经提到的

您应该尝试适应 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

当你用

p x.ometh(1)
p x.ometh('a')

你得到

    "Called me with one parameter (1)"
    "Called me with one string parameter (\"a\")"
于 2012-04-14T21:48:39.373 回答
1

很少有 gem 可以为您的 ruby​​ 代码提供此功能

  1. 功能红宝石
       defn(:问候,:男){
         “喂,先生!”
       }

       defn(:问候,:女){
         “你好,女士!”
       }

       foo.greet(:male) => "您好,先生!"
       foo.greet(:female) => "你好,女士!"

你可以从这里找到更多类似 Elixir 的模式匹配功能

  1. 合同.ruby
       合约 1 => 1
       事实 x
         X
       结尾

       合约 C::Num => C::Num
       事实 x
        x * 事实(x - 1)
       结尾

这个 gem 有助于纠正漂亮的防御代码。有一些关于性能的批评。所以基准和决定。更多示例

于 2019-01-06T06:37:33.097 回答
-1

重载的定义特征是调度是静态发生的。在 Ruby 中,调度总是动态发生的,没有其他方法。因此,在 Ruby 中重载是不可能的。

于 2012-04-14T23:22:26.050 回答