2009-03-04 10 views
5

を説明誰かがRubyコードのこの部分を説明してくださいでした:Rubyのコードは

def add_spec_path_to(args) # :nodoc: 
    args << {} unless Hash === args.last 
    args.last[:spec_path] ||= caller(0)[2] 
end 

Iは、連結文字列または他の言語でビット演算子として使用<<演算子を見てきましたが、誰かがこの文脈でそれを説明することができます。どういうわけかargsに空白のlamdaを追加していますか、まったく間違っていますか?

私はまた、このように使用見ることができます:

before_parts(*args) << block 

Hashキーワードですか?

私はまた、||=オペレータが何を言っているのかよくわかりません。

私は暗闇の中で、caller(0)[2]と同じです。

答えて

11

|| =は、共通のRubyイディオムです。これは、まだ設定されていない場合にのみ値を割り当てます。効果がオーバーライドされない===、

if some_variable == nil 
    some_variable = some_value 
end 

又は

some_variable= some_value unless some_variable 

ようなコードと同じであり、同一のための2つのオブジェクトを比較します。 Hash === args.lastの場合、Class型のオブジェクトであるHashはargs配列の最後の項目のクラスと一致するかどうかをチェックします。このコードでは、クラス#===の実装が比較対象のクラスのチェックを強制するという明らかな事実を利用しています。

これは、例えば、周囲の他の方法は動作しない。

a = [{}] 
Hash === a.last #=> true 
a.last === Hash #=> false 

方法の後続の引数は{}

を提供しなくても、ハッシュのコンテンツとして供給することができますしたがって、これを行うことができます:

def hello(arg1, arg2, arg3) 
    puts [arg1.class, arg2.class, arg3.class].join(',') 
end 

hello 1,2,3 #=> Fixnum,Fixnum,Fixnum 
hello :a, "b", :c => 1, :d => 99 #=> Symbol,String,Hash 

これは、オプションのパラメータの可変長リストを関数に提供するためによく使用されます。

元のコードを正確にbtw書き換えてもよろしいですか?引数の配列を取得するには、通常、宣言されているように引数に*を追加します。そうでなければ、オブジェクトを無効にする場合はargsを配列として入力する必要があります。

def add_spec_path_to(*args)    # now args is an array 
    args << {} unless Hash === args.last # if trailing arguments cannot be 
             # interpreted as a Hash, add an empty 
             # Hash here so that following code will 
             # not fail 
    args.last[:spec_path] ||= caller(0)[2] # Set the spec_path option if it's not 
             # already set 
end 

EDIT:*引数の事でさらに拡大し、これを試してみてください。

def x(*args) 
    puts args.join(',') 
    puts args.map{|a| a.class }.join(',') 
end 

x 1,2,:a=>5,:b=>6 
1,2,a5b6 
Fixnum,Fixnum,Hash 

... *引数を使用すると、引数が配列としてメソッドに提示されます。私は、例えば、次のように、*を使用しない場合:

def y(args) 
    puts args.join(',') 
    puts args.map{|a| a.class }.join(',') 
end 

を...そしてargsが、私はメソッドを呼び出す前に、配列である必要があり、または私が「例外ArgumentError取得します:の間違った番号を一つの事を除いて何かのための "議論"を渡しただから、このようにしなければなりません:

... {}で明示的に作成されたハッシュを使用しています。そしてそれは醜いです。

上記の全ては、MRI 1.8.6、btwで試験した。少し短いように

+0

はありません、配列に渡すと、理にかなっています。 – rampion

+0

私は理解していません。some_method([arg1、arg2、arg3]、options_hash)のようにメソッドを呼び出しますか?それは恐ろしいです:上記の編集された答えを参照してください... –

+0

素晴らしい説明、マイク。 –

14

私はargsArrayであると推測しています。

Hashは、クラスの名前である - argsの最後の要素が既にHash(オブジェクトが特定のクラスのものであるかどうかをクラス試験用===演算子)であるない限り、最初の行は、argsに空のハッシュ{}を押下します。

||=オペレータが+=演算子に似ている:それは多かれ少なかれ同等だ:だから

args.last[:spec_path] = args.last[:spec_path] || caller(0)[2] 

、それがあればargs.last[:spec_path]を設定し、それが現在設定されていない場合に限ります。

callerメソッドは、呼び出し元のメソッドに関する情報を返します。

1

:引数が返されていないので、それ以外の場合は添付のハッシュが失われてしまうので、

def add_spec_path_to(args) # :nodoc: 

... 

# Append an empty hash to args UNLESS the last arg is a hash.. in which case do nothing 
args << {} unless Hash === args.last # so we need a hash. If it is not there, make an empty one and put it there. 

... 

#if args.last[:spec_path] equals nil or false, set it to caller(0)[2]... 

#so inside that hash from the first part, if :spec_path is not there, create it by using caller(0)[2]. 

args.last[:spec_path] ||= caller(0)[2] 

... 

end