From: Robert Klemme Date: 2007-09-17T18:53:15+09:00 Subject: Re: Modifying a hash key 2007/9/17, Alex Shulgin : > On Sep 17, 3:42 am, Yukihiro Matsumoto wrote: > > Hi, > > > > In message "Re: Modifying a hash key" > > on Sun, 16 Sep 2007 17:10:05 +0900, Alex Shulgin writes: > > > > |This suggests me that the keys are actually stored in the hash by > > |reference, not by value as I would expect from my prior experience > > |with other languages. > > > > Hash stores its values, as its name suggests, by hash values from > > keys. So, if you modifies the key, and subsequently the hash value of > > the key, it screws up. As a general rule, don't modify the keys, or > > if you really need to modify the key for some unknown reason, call > > rehash method on the hash. > > So the real answer to my question (what is the reason for hash to > store it's keys by reference?) might be: trading rule of a thumb for > speed and memory? No, the reason is that you can only store by reference in Ruby (there are some internal optimizations for Fixnum and the like but the code still basically behaves the same). > If no one is ever going to modify the hash key, > there is no reason to copy it... There is also the issue that a key's hash value will likely change if you change a Hash key. So it is *never* a good idea to modify a Hash key. Note though that there is an exception: Strings are cloned if they are not frozen to avoid nasty effects. > OK, I think I got it. :-) > > If someone wonder how could I come up with modifying the hash keys: > this is my second program in Ruby and was coding a Ruby version of a > speech generator example from the Pike & Kernigan's "Practical > programming". Here is the code snippet: > > state={} > prefix=[] > while $stdin.eof? Are you serious about the line above? I'd rather have expected "until" there. I'd do this, note all the freezing in order to make errors with changing keys obvious. state = Hash.new {|h,k| h[k]=[]} prefix = [].freeze ARGF.each do |line| line.scan /\w+/ do |word| state[prefix] << word.freeze (prefix += [word]).freeze # or: prefix = (prefix.dup << word).freeze end end Btw, there's probably a more efficient way of storing this if you introduce a specialized class for prefix chaining. Probably like this: Prefix = Struct.new :word, :previous > w=... # read a word > state[prefix] << w > ... > prefix << w # <-- Bang! The hash key is changed... :-) You can #dup the prefix or use +: prefix += [w] Kind regards robert This will implicitly create a new Array. Kind regards robert