2012-07-16 5 views
5

Tôi muốn theo dõi số lượng của một số chuỗi được đặt tên theo quy luật và sau đó đặt lại số đếm thành 0. Suy nghĩ của tôi là phải làm như sau:Đặt giá trị băm bằng một giá trị băm khác trong Ruby

reset_hash={"string1"=>0,"string2"=>0,"string3"=>0} 
=> {"string1"=>0, "string2"=>0, "string3"=>0} 

new_hash = reset_hash 
=> {"string1"=>0, "string2"=>0, "string3"=>0} 

new_hash["string1"]=1 
new_hash["string3"]=1 
new_hash 
=> {"string1"=>1, "string2"=>0, "string3"=>1} 

...

Bây giờ tôi muốn đặt lại new_hash trở lại reset_hash:

new_hash = reset_hash 
=> {"string1"=>1, "string2"=>0, "string3"=>1} 
reset_hash 
=> {"string1"=>1, "string2"=>0, "string3"=>1} 

gì đang xảy ra ở đây? Dường như reset_hash đã được đặt thành new_hash, điều ngược lại với những gì tôi muốn. Làm cách nào để triển khai hành vi mong muốn?

Trả lời

5

Khi bạn có một biến trỏ đến một đối tượng, bạn thực sự chỉ có một tham chiếu đến đối tượng. Nếu cả a và b trỏ đến băm {1 => 3, "foo" => 54}, việc thay đổi a hoặc b sẽ thay đổi giá trị kia.

Tuy nhiên, bạn có thể sử dụng kết hợp hai phương pháp để thực hiện những gì bạn muốn.

Một giá trị mặc định cho các hash:

new_hash = Hash.new(0) 

này cho giá trị sử dụng một giá trị mặc định 0:

new_hash["eggs"] # -> 0 

Sau đó bạn có thể thêm số lượng:

new_hash["string1"] += 1 # => 1 

Khi bạn được thực hiện chỉ cần gọi

new_hash.clear # => {} 

và băm của bạn sẽ được đặt lại, nhưng truy cập mới vẫn sẽ mặc định 0.

Lưu ý rằng nếu bạn thiết lập giá trị mặc định cho một loại đối tượng khác hơn là một số bạn có thể thay đổi mọi thứ do toàn bộ vấn đề tham chiếu đã nêu ở trên.

irb(main):031:0> b = Hash.new("Foo") #=> {} 
irb(main):032:0> b[3] #=> "Foo" 
irb(main):033:0> b[33] #=> "Foo" 
irb(main):034:0> b[33].upcase! #=> "FOO" 
irb(main):035:0> b[3] # => "FOO" 

Để khắc phục điều này, bạn có thể vượt qua một khối để bạn băm:

h = Hash.new {|hash, key| hash[key] = "new default value"} 

Điều này tạo ra một đối tượng mới ở chính mỗi thời gian, do những thay đổi bên này sẽ không gợn:

d = Hash.new{ |hash,key| hash[key] = "string"} #=> {} 
d[3] # => "string" 
d[3].upcase! #=> "STRING" 
d[5] #=> "string" 
+0

Cảm ơn! Câu trả lời rất nhiều thông tin. –

2

Bạn đang sửa đổi một băm đơn.

Cả hai biến đều tham chiếu đến cùng một giá trị băm. Khi bạn thay đổi một mục trong giá trị băm, cả hai tham chiếu sẽ phản ánh thay đổi đó – vì nó là cùng một tính năng băm.

Có lẽ bạn muốn sao chép băm đầu tiên? Nếu bạn làm thế, và bạn có một băm với các đối tượng phức tạp, bạn cũng sẽ cần phải điều tra các bản sao/nhân bản nông so với sâu.

+0

Tôi nghĩ rằng tôi đã sao chép băm. Vậy làm thế nào để tôi làm điều đó một cách chính xác? –

+0

Bạn đang sao chép * tham chiếu * vào băm; bạn cần phải sử dụng ['clone' (docs)] (http://apidock.com/ruby/Object/clone), chú ý đến sự cẩn thận. Có rất nhiều cách để làm một bản sao sâu, bao gồm chỉ marshalling/unmarshalling đối tượng. –

1

Bạn cần sử dụng bản sao để sao chép.

Xem https://stackoverflow.com/a/4157438/1118101

Nếu không, bạn chỉ tạo ra 2 "con trỏ" để cùng bảng băm, không sao chép nội dung.

Sau đó, sử dụng thay thế để sao chép nội dung nhân bản trở lại vào băm hiện tại của bạn.

+0

OK. Cảm ơn! –

5

Vì những người khác đã đề cập đến bạn phải sử dụng clone. Nhiệm vụ của bạn sẽ trông giống như sau:

reset_hash={"string1"=>0,"string2"=>0,"string3"=>0} 
new_hash = reset_hash.clone 

new_hash["string1"]=1 
new_hash["string3"]=1 
new_hash 

new_hash = reset_hash.clone 
reset_hash