2012-04-14 9 views
7

Rubyで他の多くの言語と同じようにオーバーロード機能を使用したいが、Ruby自体はこの機能をサポートしていない。Rubyでオーバーロードする

*args引数を持つメソッドを定義する方法を使用して実装し、メソッド内の引数の数と型を判別する必要がありますか?いくつかのように:

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

あなたはそれが直接オーバーロードよりも醜いことがわかります。

私は、よりエレガントな方法でオーバーロードメソッドを定義することを可能にするキーワードや他の方法(メタプログラミングモジュールのような)があるかどうかを知りたいと思います。

+1

私は長さオプションが好きではありません... argsはnilとして渡すことができます。少なくともこれは読みにくいです。 – Arth

+3

あなたはRubyの方法に適応しようとする必要があります。 Rubyに過負荷がない理由があります。メソッドはただ一つのことを行うべきであり、異なる引数のためにまったく異なることを魔法のように決めるのではありません。代わりに、[Duck Typing](http://en.wikipedia.org/wiki/Duck_typing)を利用しようとしてください。疑わしい場合は、意味のある名前でさまざまな方法を使用してください。 –

+0

さらにお問い合わせ:いくつかのパラメータでオーバーロードする(例のように)か、パラメータのタイプを使用するか?例えば、メソッドは 'a(1)'と 'a( 'x')'を同じ呼び出しか、呼び出しを別の方法で実行していますか? – knut

答えて

4

など、より具体的名称、あなたがあなたの目標を達成するために、いくつかのメタプログラミングを試みることができるとオーバーロードメソッドの名前を変更することができます。

は、次のコードを参照してください:

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であなたのオーバーロードされたメソッドを定義することができます。パラメータは、メソッド名とプロシージャのリストです。メソッドが作成され、対応するメソッドが呼び出されます。どのメソッドがパラメータの数によって決まるか(タイプしない!)

代替構文は次のようになります。

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_overloadoverload_methodは1「過負荷法」を定義して、あなたのオーバーロードされたメソッドのためのフレームを定義します。

しかし、すでにmentioned by Holger

あなたはRubyのやり方に適応しようとする必要があります。 Rubyに過負荷がない理由があります。メソッドはただ一つのことを行うべきであり、異なる引数のためにまったく異なることを魔法のように決めるのではありません。代わりにダックタイピングを利用しようとすると、疑わしい場合は意味のある名前で別のメソッドを使用してください。


私はタイプ敏感オーバーロードとバージョンを実装する方法に興味がありました。ここにある:あなたが

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

でそれを呼び出すと

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 

あなたは

"Called me with one parameter (1)" 
    "Called me with one string parameter (\"a\")" 
+0

あなたの方法はとても美しく、私の要求を完全に満たすことができます。しかし、ホルガーの助言のために、私は自分のやり方を変えることに決めました。 :) –

+0

私はそれがより良い決定だと思う;)その間に私はタイプセンシティブオーバーロードソリューションを見つけた。私は答えを広げた。 – knut

+0

私は自分のコードから宝石を作ると考えました。他の誰かがすでに同じアイデアを持っていたようです:http://rubygems.org/gems/overloadこのソリューションは、同様のロジックを使用しています。 – knut

5

渡されていない場合は、nilに設定されているため、各引数の存在を個別にテストできます(順番に渡されたと仮定します)。

あなたが非常に異なる議論を主張するなら、私はあなたが意図する各議論のための記号でハッシュ議論を提案します。

** UPDATE **

また、あなたも、このような

def perform_task_with_qualifier_1 
+1

+1のハッシュ引数の提案。メソッドのオーバーロードを使用するほとんどすべての場合、名前付き/デフォルトの引数が明確になります。 – millimoose

+0

最初の方法は良いです。しかし、私はあなたが言った前の方法については考えていませんが、他のよりよい方法が存在するかどうかまだ不思議です。 –

+0

ああ、申し訳ありませんが、名前を変更する方法は、私が避けようとしているものです。 –

0

を取得するオーバーロードの決定的な特徴は、発送は静的に起こるということです。 Rubyでは、ディスパッチは常にが動的に発生しますが、それ以外の方法はありません。したがって、Rubyではオーバーロードは不可能です。

関連する問題