2016-11-24 8 views
2

私はルビーkoansを通じてつもりだと、このコードが実行されるとき、私は少し問題の理解を持っています:Hash.newに渡されたブロックまたはオブジェクトは、いつ作成または実行されますか?

hash = Hash.new {|hash, key| hash[key] = [] } 

ハッシュでの値がない場合は、新しい配列が与えられたに割り当てられますないときハッシュの鍵?ハッシュ値に最初に割り当てずにアクセスするのは初めてですか?指定されたハッシュキーのデフォルト値が正確に作成されたときに理解してください。

+0

ヒント:ブロックに 'puts'を追加すると、*正確に*が実行されたときにすぐに表示されます。 –

答えて

1

Rubyの新機能のために、私はこの問題の実体であるものも含めて、この問題に対する別のアプローチについて議論しました。

あなたは、配列

arr = [[:dog, "fido"], [:car, "audi"], [:cat, "lucy"], [:dog, "diva"], [:cat, "bo"]] 

に与えられたハッシュに

{ :dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"] } 

まずソリューションを作成することを希望していると仮定したタスク

h = {} 
arr.each do |k,v| 
    h[k] = [] unless h.key?(k) 
    h[k] << v 
end 
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

これはかなり簡単です。

h = {} 
arr.each { |k,v| (h[k] ||= []) << v } 
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

Rubyが見(h[k] ||= []) << v彼女が最初に行うことはhがない場合

(h[k] = h[k] || []) << v 

に展開です:

第2の解決策は、

ルビーのようなよりは書くことですキーがありませんkh[k] #=> nil、したがって式beco MES

(h[k] = []) << v 

右側h[k]Hash#[]を採用し、一方、等式の左側にh[k]は、方法Hash#[]=を使用するように

h[k] #=> [v] 

注なる

(h[k] = nil || []) << v 

この解決方法では、ハッシュ値のいずれもnilに等しくないことが必要です。

第三の溶液

第三のアプローチは、ハッシュにデフォルト値を与えることです。ハッシュhにキーkがない場合、h[k]がデフォルト値を返します。デフォルト値には2種類あります。空の配列は、Hash::newに引数として渡された場合Hash::new

に引数としてデフォルト値を渡す

、その値はデフォルト値となる:

a = [] 
a.object_id 
    #=> 70339916855860 
g = Hash.new(a) 
    #=> {} 

g[k]戻る[]hキーはkではありません。 (ただし、ハッシュは変更されません)。この構造は重要な用途を持っていますが、ここでは不適切です。理由を知るために、我々は:cat:dogの値は両方とも同じオブジェクト、空の配列に等しく設定されているためです

x = g[:cat] << "bo" 
    #=> ["bo"] 
y = g[:dog] << "diva" 
    #=> ["bo", "diva"] 
x #=> ["bo", "diva"] 

書くと仮定します。私たちは、object_id Sを調べることによって、これを見ることができます:

x.object_id 
    #=> 70339916855860 
y.object_id 
    #=> 70339916855860 

がデフォルト値デフォルト値の二番目の形式は、ブロックの計算を実行することである

を返すブロックHash::newを与えます。私たちはブロックでハッシュを定義する場合:

h = Hash.new { |h,k| h[key] = [] } 

そしてh場合は、キーkh[k]この場合、空の配列には、ブロックによって返された値に等しく設定されていません。ブロック変数hは、新しく作成された空のハッシュです。これは私たちが

h = Hash.new { |h,k| h[k] = [] } 
arr.each { |k,v| h[k] << v } 
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

ブロックに渡される最初の要素はarr.firstであるように書くことができ、ブロック変数が

k, v = arr.first 
    #=> [:dog, "fido"] 
k #=> :dog 
v #=> "fido" 

ブロック演算を評価することによって値が割り当てられしたがって

h[k] << v 
    #=> h[:dog] << "fido" 

ありますhは(まだ)キー:dogを持っていないので、ブロックがトリガーされ、h[k] equ空の配列を「FIDO」が付加されていることを[]、次いでに対するAl、同様

h #=> { :dog=>["fido"] } 

ように、arrの次の二つの要素は、我々は

h #=> { :dog=>["fido"], :car=>["audi"], :cat=>["lucy"] } 

とき次を有するブロックに渡された後に(第四)arrの要素がブロックに渡され、私たちは

h[:dog] << "diva" 

を評価したが、今hは、鍵を持っているので、DEFA最後にarrの最後の要素が処理されます。ハッシュ::ブロックで新しいを使用しているとき、私たちはこのような何かを書くことができ、

注:場合h[k]launch_missilesの戻り値に等しく設定されるだろう

h = Hash.new { launch_missiles("any time now") } 

します。言い換えれば、何でもブロック内で行うことができます。

さらにルビーのような

最後に、

h = Hash.new { |h,k| h[k] = [] } 
arr.each { |k,v| h[k] << v } 
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

を書くよりルビーのような方法がEnumerable#each_with_objectを使用することです:

2行のコードを排除
arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |k,v| h[k] << v } 
    #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]} 

どちらが最適ですか?

私は個人的に、第2および第3の解決策には無関心です。両方とも実際に使用されます。

1

ブロックは、新しいキーをハッシュに追加するときに呼び出されます。その特定の場合:詳細については

hash["d"] #calls the block and store [] as a value of "d" key 
hash["d"] #should print [] 

、訪問:ブロックが指定されている場合はhttps://docs.ruby-lang.org/en/2.0.0/Hash.html

を、それがハッシュオブジェクトとキーで呼び出され、デフォルト値を返す必要があります。必要に応じて値をハッシュに格納するのはブロックの責任です。

0

人生はこれを使用すると、値すべてのアレイであり、ハッシュキーがすでにあるかどうかを確認するには、各時間をチェックしたくないハッシュを持っているそれらの時のためのシンタックスシュガーである

が容易とします空の配列は、新しい要素を追加する前に既に初期化されています。これに代えて

hash[:new_key] << new_element

hash[:new_key] = [] unless hash[:new_key] 
hash[:new_key] << new_element 

がまた見えハッシュのデフォルト値を指定する簡単な方法の代替だ古い問題

を解きます。それは、このことができますこのように:

hash = Hash.new([])

このアプローチの問題は、同じ配列オブジェクトがすべてのキーのデフォルトとして使用されることです。だから、

hash = Hash.new([]) 
hash[:a] << 1 
hash[:b] << 2 

hash[:a]またはhash[:b]、またはそのことについても、hash[:foo]のいずれかに[1, 2]を返します。これは通常、望ましい/期待される行動ではない。

関連する問題